Webhooks Guide

Last Updated: October 15, 2025

1. Overview

Webhooks let you react to assignment activity in real time. Whenever a resource is scheduled, updated, or removed, ResourcePlanner sends an HTTP POST to your URL.

2. Enabling webhooks

  1. Open Settings → Webhooks.
  2. Enter the HTTPS endpoint that should receive events.
  3. (Optional) Provide a shared secret to sign payloads with HMAC-SHA256.

You can rotate the URL or secret any time. Updates take effect immediately.

3. Delivered events

| Event | Triggered when… |
| ----- | --------------- |
| `assignment.created` | A new assignment is created via the UI or REST API. |
| `assignment.updated` | An existing assignment changes (dates, hours, note, project/resource reassignment). |
| `assignment.deleted` | An assignment is removed. |

All events include the full assignment payload so you can mirror the ResourcePlanner state without another API call.

4. Request format

Every webhook POST has the following headers:

| Header | Description |
| ------ | ----------- |
| `Content-Type` | Always `application/json`. |
| `X-ResourcePlanner-Event` | Event name (e.g. `assignment.updated`). |
| `X-ResourcePlanner-Signature` | Hex-encoded HMAC SHA-256 signature (present only when a webhook secret is configured). |

The JSON body looks like this:

{
  "event": "assignment.created",
  "workspaceId": "64f1088d4f3c2c0012b12345",
  "timestamp": "2025-10-15T09:21:34.512Z",
  "data": {
    "id": "6717a347e1577b0b4e9fd200",
    "project": { "id": "6702ac44e1577b0b4e9fd100", "title": "Website Refresh" },
    "resource": { "id": "66fa6f3be1577b0b4e9fd001", "name": "Hana Sato" },
    "dateFrom": "2025-10-20T00:00:00.000Z",
    "dateTo": "2025-10-24T00:00:00.000Z",
    "hoursPerDay": 6,
    "startFrom": "09:00",
    "note": "Workshop preparation",
  }
}

5. Verifying signatures

When a secret is configured, compare the X-ResourcePlanner-Signature header with your own HMAC computation:

const crypto = require('crypto');

function verifySignature(secret, payload, signature) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature || '', 'hex'),
    Buffer.from(expected, 'hex')
  );
}

Use a timing-safe comparison to avoid leaking information if the signature fails.

6. Error handling & retries

Webhooks fire in the background and do not block the UI or API responses. If your endpoint returns a non-2xx status code or is unreachable, the event is logged but not retried automatically. We recommend making your handler idempotent and returning quickly (e.g. enqueue work for processing).