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 (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:
Code
Signing — Standard Webhooks
Every delivery is signed following the Standard Webhooks specification. Each request carries three headers:
webhook-id— unique message id; matchesidin the bodywebhook-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:
Code
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.
Best practices
- Verify the signature before trusting the payload.
- Return
2xxquickly; do heavy work asynchronously. The response body is ignored. - Expect retries and rare duplicates — make handling idempotent on
webhook-id. - Store the
secretsecurely; 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.
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 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. |