Webhook taxonomy
agreement.transitioned and agreement.notification.triggered are the only subscribable event types. A single subscription can receive either or both.
agreement.transitioned is delivered for every finalized input, including
inputs that do not change the state name: such deliveries carry
fromState equal to toState. Treat the event as “a finalized input
advanced this agreement”, not strictly “the state name changed” — it is the
signal to re-read agreement state and input history. Notification rules are
unaffected: they fire only on genuine state entry.
Webhook deliveries are informational. Do not trigger value transfers or
other irreversible actions from a delivery alone: on-chain value movement
belongs in the agreement’s own on-chain actions, where the chain enforces
it, and anything irreversible on your side should be confirmed against
on-chain state rather than a delivered event payload.
How webhooks are managed
You can manage webhook subscriptions in the Developer Portal or through the Agreements API and TypeScript SDK. Both paths manage subscriptions for the same Shodai account. The API-key path uses/v0/webhooks, while the Developer Portal uses your signed-in account. If a webhook is created with an API key, createdByApiKeyId is audit context, not the ownership boundary. The response field principalId identifies the owning Shodai account.
Webhook management requires webhooks.read and webhooks.write access for the account. For key setup and 401 or 403 troubleshooting, see Authentication.
Before you start
You need:- a Shodai API key or Developer Portal access
- a public HTTPS endpoint that can receive
POSTrequests - server-side access to the exact raw request body before JSON parsing
- a secure place to store the webhook signing secret returned at creation
Register a webhook
Use the TypeScript client when you are already using the SDK:secret once. Store it immediately in your application’s secret manager.
For raw HTTP integrations, call POST /v0/webhooks with the same url, optional eventTypes, and optional filters fields. Use the generated Webhooks pages in the API Reference group for exact request and response schemas.
Choose event types
On create, omitted,null, or empty eventTypes defaults the subscription to agreement.transitioned.
On update, omitted eventTypes leaves the existing event types unchanged. An explicit empty or null eventTypes value resets the subscription to agreement.transitioned.
Subscribe to agreement.notification.triggered only when your integration also attaches external_webhook notification rules to agreements. A subscription alone does not create notification-triggered events. See Notification webhooks.
Verify deliveries
Prefer the SDK receiver helper instead of hand-rolling signature verification:x-shodai-webhook-idx-shodai-webhook-timestampx-shodai-webhook-signature- timestamp tolerance, which defaults to
300seconds - event envelope shape, supported
apiVersion, supported event type, and header/body event ID match
sha256=<hex>. The signed message is ${timestamp}.${rawBody}, where timestamp is the x-shodai-webhook-timestamp header and rawBody is the exact request body bytes. Shodai signs the message with HMAC-SHA256 and the subscription secret.
Respond to deliveries
Return a2xx response only after signature verification and durable receipt. A common pattern is:
- verify the signature with
constructWebhookEvent(...) - insert the event into durable storage keyed by event
id - treat duplicate event IDs as already received
- return
2xx - process business side effects asynchronously
id is an opaque deterministic delivery identity. Retries and redeliveries for the same subscription carry the same id and payload; store it as an idempotency key instead of parsing its format. Delivery remains at-least-once, so duplicate attempts are expected.
Understand the event envelope
Every webhook delivery uses the same event envelope:data shape depends on type.
agreement.transitionedcontains compact transition data. See Agreement activity webhooks.agreement.notification.triggeredcontains interpolated notification content for one resolved rule-recipient pair. See Notification webhooks.webhook.testcontains an emptydataobject.
Filter deliveries
Filters apply before delivery. Filter values are exact string matches. Multiple values inside one filter field act like OR. Different filter fields combine like AND. Empty or omitted filters mean all subscribed events for the account. A supplied filter key must contain at least one non-blank value; an empty array or an array containing a blank or whitespace-only member is rejected with 400.
Temporal notification webhooks do not include a
transition block. They do not match inputIds, fromStates, or toStates filters.
Test a webhook
Useclient.testWebhook(...) or POST /v0/webhooks/{id}/test to send a real signed webhook.test delivery to the subscription URL:
ok is true only when that attempt succeeds. Failed tests can include status, responseStatus, and error. Disabled subscriptions cannot be tested.
Disable a webhook
Useclient.deleteWebhook(webhookId) or DELETE /v0/webhooks/{id} to disable a subscription:
status: "disabled". It does not hard-delete the subscription record.
Handle failures and retries
Delivery status depends on your receiver response:
By default, Shodai makes up to
5 total delivery attempts. Retry scheduling is checked about every 60 seconds. Backoff starts at 60 seconds and doubles, capped at 60 minutes. Redirects are not followed.
If a subscription is disabled or missing before a retry, the pending delivery is marked failed.
Use public HTTPS URLs
Webhook URLs must be valid HTTP or HTTPS URLs and must not include credentials. In normal hosted usage, Shodai requires HTTPS and rejects localhost or private-network targets, including hosts that resolve to private addresses. For local development, expose your local receiver through a public HTTPS tunnel and register the tunnel URL.Troubleshooting
Related pages
- Agreement activity webhooks
- Notification webhooks
- TypeScript client
- Authentication
- Use the Webhooks pages in the API Reference group for generated endpoint schemas.