Webhooks

Security & delivery

The signing and delivery contract for available webhook event families.

Signing & verification

AvailableHMAC-signing a payload so a receiver can verify it genuinely came from DigitalStack.

The frozen v1 contract: the signed input is the exact ASCII bytes v1:<unix-seconds>.<raw request body>, the MAC is HMAC-SHA256 encoded as lowercase hex, and it is sent in the versioned X-DStack-Signature header as t=<unix-seconds>,v1=<sig>. During a 24-hour secret-rotation overlap, two v1 signatures are sent (new + previous); accept the delivery if either verifies.

Illustrative delivery headers
POST <your endpoint URL>
Content-Type: application/json
X-DStack-Signature: t=<unix-seconds>,v1=<lowercase-hex-hmac>
X-DStack-Event: time_entry.created
X-DStack-Delivery: evt_abc123

# Signed input (exact bytes): v1:<unix-seconds>.<raw request body>
# During a secret-rotation overlap, two v1 signatures are sent:
#   X-DStack-Signature: t=<unix-seconds>,v1=<new-sig>,v1=<previous-sig>
# Accept the delivery if ANY v1 verifies.

The header names, exact signed bytes, and signature encoding are frozen and covered by deterministic cross-language test vectors.

Verification & secret details

Signed timestampsecurityoptional
A base-10 Unix-seconds timestamp is part of the signed input and the t= field of the signature header. Reject deliveries whose timestamp falls outside your own freshness tolerance.
Replay protectionsecurityoptional
DigitalStack is the sender and does not itself reject replays. Receiver-side defense is the signed timestamp plus the immutable delivery id (X-DStack-Delivery / envelopeevent_id): enforce a freshness window and dedupe on the id.
Endpoint validationsecurityoptional
Registration and every delivery attempt run a shared DNS-resolving SSRF guard (HTTPS-only, approved ports, rejects private/loopback/link-local/multicast/metadata and IPv4-mapped IPv6, no redirects, bounded response reads). It does not verify you control the endpoint, and a narrow DNS-rebinding window remains until the outbound connection is pinned to the validated address.
Secret rotationsecurityoptional
Available: a distinct, audited rotate action regenerates the signing secret (returned once) and keeps the previous secret valid for a 24-hour overlap, during which deliveries are dual-signed so a receiver can migrate without downtime. Rotation idempotency keys are scoped to a single webhook subscription: reusing a key for the same subscription returns the original operation's completion state (the secret is never shown again); the same key may be used independently for another subscription.
Secret at restsecurityoptional
The signing secret is encrypted at rest (AES-256-GCM, versioned key, bound to the subscription and workspace) — shown once at creation and never retrievable afterward.
IP allowlistingsecurityoptional
Not implemented. There is no fixed, published set of source IPs to allowlist for inbound delivery.

Delivery behavior

AvailableAutomatic re-attempts with backoff when a delivery fails.
Retry policydesignedoptional
Up to 3 attempts with exponential backoff (2s, then 4s) between attempts.
Timeoutdesignedoptional
Each delivery attempt is aborted after 10 seconds if the endpoint hasn't responded.
Delivery orderingdesignedoptional
Not documented or guaranteed across events. Deliveries to multiple subscribed endpoints for the same event fire concurrently.
Duplicate deliverydesignedoptional
Possible in principle on retry (e.g. your endpoint processed a request but the response was lost). Use the delivery id for idempotency once live.
Failure handlingdesignedoptional
Any non-2xx response or timeout counts as a failed attempt; after the max attempt count, the delivery is recorded as failed.
Replay / redeliverydesignedoptional
A governed redelivery endpoint enqueues a new delivery job for the original subscription, preserves the immutable event payload, and audits the request without storing the raw idempotency key.
Delivery log retentionavailableoptional
Delivery jobs and attempts are inspectable through the delivery history API. A formal retention period is not yet published.

Authorization

webhooks:manage is now enforced

Both session and API-key/OAuth callers can create a subscription; an API key or delegated token must hold `webhooks:manage` and is bound to its own workspace regardless of the credential owner's other memberships. Session users can also use the in-product page at /app/developer/webhooks.
webhooks:manageAPI key scope — enforcedoptional
Create, update, delete, and inspect webhook registrations.An API key or delegated OAuth token must hold this scope to call any webhook subscription route, and is authorized only against the workspace it was issued for — a key issued for one workspace cannot manage another workspace's webhooks even if its owner is a member of both. Session-authenticated (in-product) requests are authorized by workspace membership instead, unchanged from before.

Subscription management

Enforced, but no in-product UI yet

Same authorization model as subscription creation: `webhooks:manage`-scoped API-key/OAuth callers and session users can both list, update, and delete, each bound to their own workspace. The in-product page covers the session path; API-key/OAuth callers use the routes directly. The routes themselves are fully authorized end to end — this is a missing front end, not a missing authorization boundary.
  1. Register an endpoint URL (HTTPS, not a private/loopback address).
  2. Select which event types to receive, or subscribe to all events.
  3. Receive a one-time signing secret, generated at registration.
  4. Inspect delivery status and disable or delete the subscription when no longer needed.

Troubleshooting

Endpoint unreachable

Delivery is attempted with a 10-second timeout per try, then retried with backoff. An unreachable endpoint appears as a failed delivery attempt in the log.

Non-2xx response

Any status outside the 2xx range is treated as a failed attempt and retried (up to the max attempt count) before the delivery is marked failed.

Timeout

A request that does not complete within 10 seconds is aborted and counted as a failed attempt.

Invalid signature

Read the raw request body BEFORE parsing, reconstruct the exact signed input v1:<unix-seconds>.<raw body>, compute HMAC-SHA256 with your webhook secret, encode as lowercase hex, and constant-time compare against each v1 value in X-DStack-Signature (accept any match during a rotation overlap). Reject timestamps outside your tolerance and dedupe on the delivery id.

Duplicate event

At-least-once delivery means duplicates are possible. Use the delivery id (X-DStack-Delivery) and immutable event_id to de-duplicate on your end.

Unsupported event

A subscription can select specific event types, or subscribe to all events (empty selection). Requesting an event type outside the documented catalog is rejected by the subscription API's validation.

Disabled subscription

A subscription has an active/inactive flag; an inactive subscription receives no deliveries. There is no public UI to toggle this today (see Subscription management below).

No events received

Confirm your subscription includes the event type, is active, and the event family is available. Time Management, Work Items, and Work Item Enhance events are available; document/workflow events are still planned.

Back to the webhooks overview or the event catalog.