Skip to main content
Webhooks are the signed delivery mechanism Shodai uses to send compact events to your backend. A webhook subscription belongs to an API principal, points to one HTTPS receiver URL, and can receive one or more subscribed event families. Use this page for subscription setup, verification, retries, testing, and shared delivery behavior. Use Agreement activity webhooks for agreement lifecycle reconciliation, and Notification webhooks for notification-rule deliveries.

Webhook taxonomy

agreement.transitioned and agreement.notification.triggered are the only subscribable event types. A single subscription can receive either or both.

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 API principal model. The API-key path uses /v0/webhooks. The Developer Portal resolves your signed-in account to the self-serve API principal and manages that principal’s subscriptions. A webhook belongs to the principal. If a webhook is created with an API key, createdByApiKeyId is audit context, not the ownership boundary. Webhook management requires webhooks.read and webhooks.write access for the API principal. 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 POST requests
  • 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:
The create response includes secret once. Store it immediately in your application’s secret manager.
Shodai does not include the signing secret when you list, get, update, disable, or test webhook subscriptions. If the secret was not stored from the create response, create a new webhook subscription.
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:
Verify the raw request body before trusting parsed JSON. The helper checks:
  • x-shodai-webhook-id
  • x-shodai-webhook-timestamp
  • x-shodai-webhook-signature
  • timestamp tolerance, which defaults to 300 seconds
  • event envelope shape, supported apiVersion, supported event type, and header/body event ID match
The signature header uses 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 a 2xx response only after signature verification and durable receipt. A common pattern is:
  1. verify the signature with constructWebhookEvent(...)
  2. insert the event into durable storage keyed by event id
  3. treat duplicate event IDs as already received
  4. return 2xx
  5. process business side effects asynchronously
Your application owns deduplication, queueing, business side effects, logging, and observability.

Understand the event envelope

Every webhook delivery uses the same event envelope:
The data shape depends on type.
  • agreement.transitioned contains compact transition data. See Agreement activity webhooks.
  • agreement.notification.triggered contains interpolated notification content for one resolved rule-recipient pair. See Notification webhooks.
  • webhook.test contains an empty data object.

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 API principal. Temporal notification webhooks do not include a transition block. They do not match inputIds, fromStates, or toStates filters.

Test a webhook

Use client.testWebhook(...) or POST /v0/webhooks/{id}/test to send a real signed webhook.test delivery to the subscription URL:
The test response reports the immediate delivery attempt. ok is true only when that attempt succeeds. Failed tests can include status, responseStatus, and error. Disabled subscriptions cannot be tested.

Disable a webhook

Use client.deleteWebhook(webhookId) or DELETE /v0/webhooks/{id} to disable a subscription:
The route disables the subscription and returns the subscription with 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