# Webhooks

Register HTTPS endpoints to receive signed event notifications. Manage endpoints
under **Marketplace → Webhooks** in the LedgerBee app, or via the API — the
**Webhook Endpoints** tag in the **[API Reference](/api)** (create, list,
update, delete, roll-secret, and a test delivery; scopes
`webhook-endpoints-read` / `webhook-endpoints-write`).

Endpoint URLs must be **public HTTPS** — plain `http` and private/loopback
hosts are rejected at creation. Creating an endpoint returns a signing secret
of the form `whsec_…`, shown **once**; store it to verify every delivery.

## Delivery envelope

Every delivery POSTs a JSON envelope — and the **whole envelope** is what gets
signed:

```json
{
  "id": "…",          // the delivery id — same value as the webhook-id header
  "type": "subscription.started",
  "timestamp": "2026-06-01T12:00:00.000Z",
  "data": { … }       // topic-specific payload
}
```

## Signing — Standard Webhooks

Every delivery is signed following the
[Standard Webhooks](https://www.standardwebhooks.com/) specification. Each
request carries three headers:

- `webhook-id` — unique message id; matches `id` in the body
- `webhook-timestamp` — send time in **unix seconds** (use it for a replay
  window; reject timestamps far outside your clock)
- `webhook-signature` — `v1,<base64 HMAC-SHA256>` over `{id}.{timestamp}.{rawBody}`
  (space-separated entries; verify against each)

## Verifying a delivery

**Do not** hand-roll the HMAC check. Use one of the Standard Webhooks drop-in
libraries with the endpoint `secret` (returned **once** when you create the
endpoint or roll its secret). Verify against the **raw request body** — don't
re-serialize the parsed JSON; whitespace changes break the HMAC:

```ts
import { Webhook } from 'standardwebhooks';

const wh = new Webhook(endpointSecret); // "whsec_..." from create/roll-secret

app.post('/ledgerbee-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    event = wh.verify(req.body, req.headers); // throws on bad signature / stale timestamp
  } catch {
    return res.sendStatus(400); // reject unverified payloads — do not process them
  }
  handle(event); // event === the parsed { id, type, timestamp, data }
  res.sendStatus(200);
});
```

## Retries

A non-`2xx` response or a timeout is retried up to **8 total attempts over
roughly 21 hours**, with exponential backoff (30s → 2m → 8m → 32m → ~2h →
~8.5h → 10h). Every retry carries the **same** `webhook-id` — but is
**re-signed with the new send time**, so verify against the headers of the
delivery you received, and deduplicate on `webhook-id`. Deliveries can arrive
out of order. After the schedule is exhausted the delivery is marked failed in
the endpoint's delivery log (**Marketplace → Webhooks**).

## Reconciling subscription events

Every `subscription.*` event's `data` carries **`partnerReferenceId`** — the
`clientReferenceId` passed when binding an embedded checkout. It's your join
key: match the event to your own order without storing LedgerBee ids first. If a delivery is missed (endpoint down, not yet subscribed), recover
over the API instead of replaying:
`GET /v1/subscriptions?partnerReferenceId=<yours>` — see
**[Subscriptions](/guides/subscriptions/webhooks)**.

## Best practices

- Verify the signature **before** trusting the payload.
- Return `2xx` quickly; do heavy work asynchronously. The response body is
  ignored.
- Expect retries and rare duplicates — make handling idempotent on `webhook-id`.
- Store the `secret` securely; roll it from **Marketplace → Webhooks** if
  exposed (deliveries re-sign with the new secret immediately).
- Use the **test delivery** (UI button or `POST /v1/webhook-endpoints/{id}/test`)
  to wire up verification before real traffic arrives.

{/* @codegen webhook-event-types — generated by `pnpm docs:generate`; do not edit by hand */}

## Event types

Subscribe an endpoint to any of the topics below. Each delivery carries the topic string in the envelope `type` field and the topic-specific payload in `data` — see the [delivery envelope](#delivery-envelope) for the wrapper.

### Subscription

#### `subscription.assigned`

Fired when a subscription is assigned to a customer (may be future-dated / upcoming).

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer the subscription was assigned to. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `assignedAt` | string (date-time) | ISO 8601 timestamp of when the subscription was assigned. |

#### `subscription.started`

Fired when a subscription becomes active — grant entitlement/access on this event, not on assigned.

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `startedAt` | string (date-time) | ISO 8601 timestamp of when the subscription became active. |

#### `subscription.updated`

Fired when a config property changes without a structural transition (e.g. name, payment method, billing direction).

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `changedFields` | string[] | Names of the subscription fields changed in this update. |
| `updatedAt` | string (date-time) | ISO 8601 timestamp of when the update was applied. |

#### `subscription.transitioned`

Fired on a structural change: plan replace, product edit, billing cadence change, or realign.

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `transitionType` | `plan_replaced` \| `products_edited` \| `billing_cadence_changed` \| `billing_realigned` | Which kind of structural change occurred. |
| `transitionedAt` | string (date-time) | ISO 8601 timestamp of when the transition was applied. |

#### `subscription.paused`

Fired when a subscription is paused.

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `pausedAt` | string (date-time) | ISO 8601 timestamp of when the subscription was paused. |

#### `subscription.resumed`

Fired when a subscription resumes from a pause.

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `resumedAt` | string (date-time) | ISO 8601 timestamp of when the subscription resumed. |

#### `subscription.trial_ended`

Fired when a subscription trial ends and normal billing begins (the sub stays active).

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `endedAt` | string (date-time) | ISO 8601 timestamp of when the trial ended. |

#### `subscription.cancellation_scheduled`

Fired when a future cancellation is scheduled for a subscription (still active until then).

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `effectiveDate` | string (date-time) or null | When the cancellation takes effect, if known. |
| `scheduledAt` | string (date-time) | ISO 8601 timestamp of when the cancellation was scheduled. |

#### `subscription.cancellation_cleared`

Fired when a previously scheduled cancellation is cleared.

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `clearedAt` | string (date-time) | ISO 8601 timestamp of when the scheduled cancellation was cleared. |

#### `subscription.churned`

Fired when a subscription is cancelled/finalized — revoke entitlement on this event.

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owned the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `reason` | string or null | Free-text cancellation reason, if one was supplied. |
| `churnedAt` | string (date-time) | ISO 8601 timestamp of when the subscription was cancelled. |

#### `subscription.billed`

Fired once per (child) subscription when a billing run produces its invoice.

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `invoiceId` | string (uuid) | Id of the invoice produced by the billing run. |
| `invoiceNumber` | string | Human-facing invoice number. |
| `amount` | string | Total invoice amount, as a decimal string. |
| `currency` | string | ISO 4217 currency code of the invoice. |
| `billedAt` | string (date-time) | ISO 8601 timestamp of when the invoice was produced. |

#### `subscription.payment_succeeded`

Fired when a card charge for the subscription clears — the success pair of the charge_failed error signal.

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `invoiceId` | string (uuid) | Id of the invoice the charge paid. |
| `invoiceNumber` | string | Human-facing number of the invoice the charge paid. |
| `amount` | string | Charged amount, as a decimal string. |
| `currency` | string | ISO 4217 currency code of the charge. |
| `occurredAt` | string (date-time) | ISO 8601 timestamp of when the charge cleared. |

#### `subscription.error`

Fired when a delivery/processing problem occurs for a subscription (e.g. undeliverable recipient).

| Field | Type | Description |
|---|---|---|
| `subscriptionId` | string (uuid) | Stable id of the subscription (the chain head id, fixed at creation and unchanged across plan / direction / cadence changes and cancellation). Correlate events to a subscription on this id. |
| `versionId` | string (uuid) | Internal version-chain row the event was emitted on. Advanced/debug only — it drifts across changes; correlate on `subscriptionId` or `partnerReferenceId`, not this. |
| `customerId` | string (uuid) | Id of the customer that owns the subscription. |
| `partnerReferenceId` | string or null | Your `clientReferenceId` from embedded checkout, echoed back for reconciliation. Null when none was set. |
| `errorType` | `recipient_undeliverable` \| `charge_failed` | Which problem occurred — see the subscription.error sub-events table for the full set and meaning. |
| `message` | string | Human-readable description of the problem. |
| `occurredAt` | string (date-time) | ISO 8601 timestamp of when the problem occurred. |

The `errorType` field discriminates which problem occurred — it is one of:

| `errorType` | Meaning |
|---|---|
| `recipient_undeliverable` | A document recipient for the subscription is undeliverable (e.g. the email hard-bounced); the document was not delivered. |
| `charge_failed` | A card charge (first or recurring) for the subscription failed; the subscription is moved to AWAITING_PAYMENT so you can gate access. The raw provider decline is not exposed on the payload. |

### Billing

#### `billing.invoice_sent`

Fired when an invoice document is sent to the customer.

| Field | Type | Description |
|---|---|---|
| `invoiceId` | string (uuid) | Id of the invoice that was sent. |
| `invoiceNumber` | string | Human-facing invoice number. |
| `customerId` | string (uuid) | Id of the customer the invoice was sent to. |
| `subscriptionId` | string (uuid) or null | Subscription the invoice relates to, or null for a standalone invoice. |
| `amount` | string | Total invoice amount, as a decimal string. |
| `currency` | string | ISO 4217 currency code of the invoice. |
| `sentAt` | string (date-time) | ISO 8601 timestamp of when the invoice was sent. |

#### `billing.credit_note_sent`

Fired when a credit note document is sent to the customer.

| Field | Type | Description |
|---|---|---|
| `creditNoteId` | string (uuid) | Id of the credit note that was sent. |
| `creditNoteNumber` | string | Human-facing credit note number. |
| `customerId` | string (uuid) | Id of the customer the credit note was sent to. |
| `subscriptionId` | string (uuid) or null | Subscription the credit note relates to, or null for a standalone credit note. |
| `amount` | string | Total credit note amount, as a decimal string. |
| `currency` | string | ISO 4217 currency code of the credit note. |
| `sentAt` | string (date-time) | ISO 8601 timestamp of when the credit note was sent. |

### Customer

#### `customer.created`

Fired when a customer is created (payload flags whether it was a portal self-signup).

| Field | Type | Description |
|---|---|---|
| `customerId` | string (uuid) | Id of the created customer. |
| `selfSignup` | boolean | True when the customer was created via portal self-signup; false for operator/API/import creation. |
| `createdAt` | string (date-time) | ISO 8601 timestamp of when the customer was created. |

### Card

#### `card.added`

Fired when a card is successfully saved for a customer (provider-opaque).

| Field | Type | Description |
|---|---|---|
| `cardId` | string (uuid) | Id of the saved card (LedgerBee CustomerPaymentMethod id). |
| `customerId` | string (uuid) | Id of the customer the card was saved for. |
| `cardMask` | string or null | Masked card number, if known. |
| `cardType` | string or null | Card network/brand, if known — never the payment provider. |
| `expiryMonth` | integer or null | Card expiry month (1–12), if known. |
| `expiryYear` | integer or null | Card expiry year (4-digit), if known. |
| `isDefault` | boolean | True when this card is the customer’s default after being saved. |
| `addedAt` | string (date-time) | ISO 8601 timestamp of when the card was saved. |

#### `card.removed`

Fired when a saved card is removed in-app or cancelled at the provider.

| Field | Type | Description |
|---|---|---|
| `cardId` | string (uuid) | Id of the removed card. |
| `customerId` | string (uuid) | Id of the customer the card belonged to. |
| `cardMask` | string or null | Masked card number, if known. |
| `cardType` | string or null | Card network/brand, if known. |
| `reason` | `removed_by_customer` \| `removed_by_provider` | Why the card was removed: removed_by_customer = removed in-app (portal/operator); removed_by_provider = cancelled out-of-band at the provider. |
| `removedAt` | string (date-time) | ISO 8601 timestamp of when the card was removed. |

#### `card.updated`

Fired when a saved card’s details change (network auto-update or refresh).

| Field | Type | Description |
|---|---|---|
| `cardId` | string (uuid) | Id of the updated card. |
| `customerId` | string (uuid) | Id of the customer that owns the card. |
| `cardMask` | string or null | Masked card number after the update, if known. |
| `cardType` | string or null | Card network/brand after the update, if known. |
| `expiryMonth` | integer or null | Card expiry month (1–12) after the update, if known. |
| `expiryYear` | integer or null | Card expiry year (4-digit) after the update, if known. |
| `isDefault` | boolean | Whether this card is the customer’s default. |
| `changedFields` | string[] | Names of the card fields that changed in this update. |
| `updatedAt` | string (date-time) | ISO 8601 timestamp of when the update was applied. |

#### `card.expiring`

Fired when a saved card is approaching expiry.

| Field | Type | Description |
|---|---|---|
| `cardId` | string (uuid) | Id of the expiring card. |
| `customerId` | string (uuid) | Id of the customer that owns the card. |
| `cardMask` | string or null | Masked card number, if known. |
| `cardType` | string or null | Card network/brand, if known. |
| `expiryMonth` | integer or null | Card expiry month (1–12), if known. |
| `expiryYear` | integer or null | Card expiry year (4-digit), if known. |
| `signaledAt` | string (date-time) | ISO 8601 timestamp of when the expiry signal was raised. |

#### `card.expired`

Fired when a saved card is observed to have expired.

| Field | Type | Description |
|---|---|---|
| `cardId` | string (uuid) | Id of the expired card. |
| `customerId` | string (uuid) | Id of the customer that owns the card. |
| `cardMask` | string or null | Masked card number, if known. |
| `cardType` | string or null | Card network/brand, if known. |
| `expiryMonth` | integer or null | Card expiry month (1–12), if known. |
| `expiryYear` | integer or null | Card expiry year (4-digit), if known. |
| `expiredAt` | string (date-time) | ISO 8601 timestamp of when the card was observed expired. |

#### `card.default_changed`

Fired when the customer’s default card changes (explicit or auto-default).

| Field | Type | Description |
|---|---|---|
| `cardId` | string (uuid) | Id of the card that is now the customer’s default. |
| `customerId` | string (uuid) | Id of the customer whose default card changed. |
| `previousDefaultCardId` | string (uuid) or null | Id of the card that was previously default, or null if the customer had no default before. |
| `changedAt` | string (date-time) | ISO 8601 timestamp of when the default changed. |

#### `card.backup_changed`

Fired when the customer’s designated backup card is set, replaced, or cleared.

| Field | Type | Description |
|---|---|---|
| `cardId` | string (uuid) or null | Id of the card that is now the customer’s designated backup, or null when the designation was cleared. |
| `customerId` | string (uuid) | Id of the customer whose backup card changed. |
| `previousBackupCardId` | string (uuid) or null | Id of the card that was previously the backup, or null if the customer had no backup before. |
| `changedAt` | string (date-time) | ISO 8601 timestamp of when the backup designation changed. |

#### `card.add_failed`

Fired when a card-save attempt fails (decline or authentication failure).

| Field | Type | Description |
|---|---|---|
| `customerId` | string (uuid) | Id of the customer the failed card-save was for. |
| `reason` | string | Machine/short reason for the failure (decline or authentication failure). |
| `failedAt` | string (date-time) | ISO 8601 timestamp of when the card-save attempt failed. |

### Quote

#### `quote.sent`

Fired when a quote is sent to the customer.

| Field | Type | Description |
|---|---|---|
| `quoteId` | string (uuid) | Id of the quote that was sent. |
| `quoteNumber` | string or null | Human-facing quote number (allocated on send). |
| `customerId` | string (uuid) | Id of the customer the quote was sent to. |
| `sentAt` | string (date-time) | ISO 8601 timestamp of when the quote was sent. |

#### `quote.converted`

Fired when a quote is converted into an order confirmation or an invoice.

| Field | Type | Description |
|---|---|---|
| `quoteId` | string (uuid) | Id of the quote that was converted. |
| `quoteNumber` | string or null | Human-facing quote number. |
| `targetType` | `invoice` \| `orderConfirmation` | What the quote was converted into. |
| `targetId` | string (uuid) | Id of the document the conversion produced (an invoice or order confirmation). |
| `customerId` | string (uuid) | Id of the customer the documents belong to. |
| `convertedAt` | string (date-time) | ISO 8601 timestamp of when the conversion happened. |

### Order confirmation

#### `order_confirmation.sent`

Fired when an order confirmation is sent to the customer.

| Field | Type | Description |
|---|---|---|
| `orderConfirmationId` | string (uuid) | Id of the order confirmation that was sent. |
| `orderConfirmationNumber` | string or null | Human-facing order confirmation number (allocated on send). |
| `customerId` | string (uuid) | Id of the customer the order confirmation was sent to. |
| `sentAt` | string (date-time) | ISO 8601 timestamp of when the order confirmation was sent. |

#### `order_confirmation.converted`

Fired when an order confirmation is converted into an invoice.

| Field | Type | Description |
|---|---|---|
| `orderConfirmationId` | string (uuid) | Id of the order confirmation that was converted. |
| `orderConfirmationNumber` | string or null | Human-facing order confirmation number. |
| `targetType` | `invoice` | Order confirmations only ever convert into an invoice. |
| `targetId` | string (uuid) | Id of the invoice the conversion produced. |
| `customerId` | string (uuid) | Id of the customer the documents belong to. |
| `convertedAt` | string (date-time) | ISO 8601 timestamp of when the conversion happened. |

### Project

#### `project.created`

Fired when a project is created (payload carries the owned dimension id for correlation with the dimensions API).

| Field | Type | Description |
|---|---|---|
| `projectId` | string (uuid) | Id of the created project. |
| `name` | string | Project name at creation time. |
| `dimensionId` | string (uuid) | Id of the dimension the project owns; usable with the dimensions API. |
| `externalReference` | string or null | Partner-supplied external reference, null when not set. |
| `createdAt` | string (date-time) | ISO 8601 timestamp of when the project was created. |

#### `project.updated`

Fired when a project's fields are updated (payload lists the changed field names). Also fires on un-archive.

| Field | Type | Description |
|---|---|---|
| `projectId` | string (uuid) | Id of the updated project. |
| `changedFields` | string[] | Names of the project fields the update changed. |
| `updatedAt` | string (date-time) | ISO 8601 timestamp of the update. |

#### `project.archived`

Fired when a project is archived (its dimension is deactivated; postings history is preserved).

| Field | Type | Description |
|---|---|---|
| `projectId` | string (uuid) | Id of the archived project. |
| `archivedAt` | string (date-time) | ISO 8601 timestamp of the archive action. |

{/* @codegen-end webhook-event-types */}
