parseWebhookEvent
Parse a webhook delivery body into the typed WebhookEvent union. Every payload Seismic delivers is validated against the same Zod schemas the rest of the SDK uses, so the resource embedded in the event (kyc_profile, kyb_profile, account, order, deposit) comes back fully typed. Narrowing on event_type narrows data with it.
Always verify the delivery's signature before parsing. Deliveries carry the signature headers svix-id, svix-timestamp, and svix-signature; verify them against your endpoint's signing secret. See Delivery semantics.
Alongside event_type and data, every parsed event carries created_at — when the event was recorded, stable across retries and replays of that event. It is typed optional because events recorded before the field existed have none to replay. See Envelope.
const = ();
switch (.) {
case "account.deposit_received": {
// `event.data` is narrowed: `deposit` is present and typed.
const { , } = .;
.(`deposit ${.}: ${.} → account ${}`);
break;
}
case "order.status_changed": {
.(`order ${..} → ${..}`);
break;
}
case "customer.kyc.status_changed":
case "customer.kyb.status_changed": {
.(`customer ${..} → ${..}`);
break;
}
}Arguments: payload: unknown. A raw JSON string or an already-parsed object.
Returns: WebhookEvent.
Throws: ZodError when the payload doesn't match any known event shape (or SyntaxError for a malformed JSON string). For a non-throwing variant, parse yourself with WebhookEventSchema.safeParse(JSON.parse(body)).
WebhookEvent
A discriminated union over the twelve event types in the catalog, keyed on event_type:
event_type | data highlights |
|---|---|
customer.kyc.status_changed | customer_id, status (KycStatus), expanded? (boolean), kyc_profile? (KycProfile) |
customer.kyb.status_changed | customer_id, status (KybStatus), expanded? (boolean), kyb_profile? (KybProfile) |
customer.virtual_account_approval.changed | customer_id, currency, status (ApprovalStatus), banks? (string[]), updated_at? |
account.created | account_id, customer_id, account_kind, account? (Account) |
account.updated | account_id, customer_id, account_kind, account? (Account) |
account.deposit_received | account_id, customer_id, account_kind, deposit (WebhookDeposit), account? |
deposit.status_changed | account_id, customer_id, account_kind, status (AccountDepositStatus), deposit (WebhookDeposit), account? |
account.deposit_returned | account_id, customer_id, account_kind, deposit (WebhookDeposit), account? |
account.deposit_liquidated | account_id, customer_id, account_kind, liquidation (WebhookLiquidation), account? |
liquidation.settlement_recorded | liquidation_id, account_id, customer_id, transaction_hash, updated_at |
order.status_changed | order_id, customer_id, status (OrderStatus), order? (same shape as accept-quote) |
order.settlement_recorded | order_id, customer_id, transaction_hash, source_from_address?, source_chain? |
The embedded resource fields are optional for the backfill caveat reason: freshly-emitted events always carry them. Replays of older deliveries may not.
Per-event schemas are also exported individually (AccountDepositReceivedEventSchema, OrderStatusChangedEventSchema, …) along with WebhookEventSchema for the whole union.
Payload reference: Customer events, Account events, Order events.
