Webhooks and events overview
How Wingspan V3 tells your system that something changed, through webhook pushes and a 30-day event log you can read back.
This page explains how Wingspan tells your system that something changed, so you can react to payments, files, and account changes without polling every resource.
Wingspan gives you two ways to receive the same events:
- Webhook push. You create a subscription with an HTTPS URL. When an event you subscribed to happens, we
POSTit to that URL, signed with a secret only you and Wingspan know. - The event log. Every event is also stored for 30 days at
GET /v3/platform/events. You read it with a cursor, from where you left off.
Push is fast but best effort. The event log is the durable record. A reliable integration uses both: push for speed, the log to catch anything push missed. The rest of this section shows how.
Coming from V1? V1 had one webhook "preference" per account at
/integrations/webhooks/preference, you chose the shared secret, and each push carried a full copy of the invoice or payee. In V3 you can have many subscriptions, Wingspan generates the secret, each push is a small reference you look up, and you can recover missed events from the event log.
What an event looks like
Every event is a small, signed reference to a resource. It tells you what happened and which resource changed. It doesn't carry the whole resource.
{
"id": "5f0c1d7e9a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
"type": "Invoice.Paid",
"routingSubject": { "type": "Account", "id": "Qm4xT8vLp2RzW6nYc0aBd3" },
"createdAt": "2026-09-24T14:30:02Z",
"eventCreatedAt": "2026-09-24T14:30:00Z",
"apiVersion": "2026-03-27",
"occurrenceId": "9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1a0f9e8d",
"payloadProjection": { "id": "WebhookResourceId", "version": 1, "sha256": "..." },
"actorProjection": { "id": "WebhookSubjectSafeActor", "version": 1, "sha256": "..." },
"publicEnvelope": { "id": "Invoice.PaidEnvelope", "version": 1, "sha256": "..." },
"data": { "id": "H7kP2wQx9LmN4vRt6yZa1b" },
"actor": { "kind": "Person", "disclosure": "Redacted" }
}| Field | What it means |
|---|---|
id | This event's unique ID, a 64-character hex string. Use it to spot duplicate deliveries. |
type | {Resource}.{Event}, for example Invoice.Paid. See Event types. |
routingSubject | The Account, Organization, or Person this copy of the event is for. |
occurrenceId | The ID of the change itself. When one change concerns two parties (a payer and a payee), each gets its own event with a different id and the same occurrenceId. |
createdAt | When Wingspan recorded the event in the event log. |
eventCreatedAt | When the change happened. |
apiVersion | The date-based version the event was written in. It never changes after the event is created. |
data | The resource's id, plus its status and externalId on events that include them. |
actor | Who made the change: Person, ServiceAccount, or System. The id is included only when you're allowed to see it; otherwise disclosure is Redacted. |
context, parent, confirmation | Extra fields on some event types. See Event types. |
The projection and envelope fields identify the exact schema the event was built from. Most consumers can ignore them.
Why events are references, not full resources
A V1 push carried the whole invoice. That made the payload large, could expose fields a recipient shouldn't see, and went stale the moment the invoice changed again. A V3 event is a pointer: when you receive Invoice.Paid, call GET /v3/payments/invoices/{invoiceId} with data.id to read the current invoice. The resource API is always the source of truth.
Scopes: whose events you receive
Every subscription, and every event log query, picks exactly one scope:
| Scope | Receives events for |
|---|---|
Account | One Account. Set shouldIncludeDescendants: true to also include its child Accounts. |
Organization | The Organization itself. Set shouldIncludeAccounts: true to also include events for Accounts in the Organization. |
Person | One Person, and only yourself. Used for Person-level events such as notifications. |
Scopes never widen on their own. An Organization scope doesn't include Person events, and an Account scope doesn't include sibling Accounts. See Create a subscription.
How the pieces fit
sequenceDiagram
participant W as Wingspan
participant Y as Your endpoint
participant Q as Your queue
W->>Y: POST event (signed)
Y->>Y: Verify signature
Y->>Q: Enqueue event
Y-->>W: 200 OK
Q->>W: GET the resource by data.id
Note over Q,W: Later, on a schedule
Q->>W: GET /v3/platform/events?page[token]=resumeCursor
W-->>Q: Any events push missed, plus a new resumeCursor
Pages in this section
- Create a subscription: choose a scope and events, create the subscription, and store the secret.
- Verify signatures: check
Wingspan-Signaturein Node.js or Python. - Delivery and retries: the retry schedule, what counts as success, and a checklist for a reliable consumer.
- Recover missed events: read the event log with a saved cursor.
- Event types: every event you can subscribe to today.
- Paid vs DepositConfirmed: what each payment event does and doesn't tell you.
Related pages
- Async operations, which emit
Operation.*events. - Errors
- Idempotency
Updated 10 days ago