Webhooks
Payloads and headers
Every webhook delivery is a compact JSON body POSTed to your endpoint, wrapped in a set of HTTP headers that identify the event and prove the delivery is authentic. This page documents both.
Delivery body
The delivery body is a compact JSON object POSTed to your endpoint. It carries the identity of the source event, the workspace it belongs to, what happened, and who triggered it.
json
{
"id": "evt_9f2c...",
"tenantId": "ten_123",
"action": "task.completed",
"target": "task:t_456",
"actorId": "usr_789",
"actorKind": "user",
"context": { },
"at": "2026-07-07T12:34:56.000Z"
}| Field | Description |
|---|---|
id | Unique id of the source event. |
tenantId | Your workspace id. |
action | The event name, same as the x-atlas-event header. |
target | The affected object, formatted type:id. |
actorId | Who or what triggered it. |
actorKind | One of user, system, or integration - the kind of actor. |
context | An object with event-specific details, may be empty. |
at | ISO-8601 timestamp. |
Headers
Every delivery carries these HTTP headers. They let you route by event type, verify the signature, and protect against replayed deliveries without parsing the body.
| Header | Value | Purpose |
|---|---|---|
content-type | application/json | Payload encoding. |
x-atlas-webhook-signature | Hex HMAC-SHA256 of `${timestamp}.${webhookId}.${body}` | Canonical, replay-safe signature (verify this one). |
x-atlas-signature | sha256=<hex HMAC of the raw body> | Legacy body-only signature, kept for backward compatibility. |
x-atlas-event | The action, e.g. task.completed | Quick event-type routing without parsing the body. |
x-atlas-delivery | <sourceEventId>:<attempt> | Identifies this delivery attempt. |
x-atlas-webhook-id | A per-delivery unique id | Used with the timestamp for replay protection. |
x-atlas-webhook-timestamp | Unix time in milliseconds | Must be within 5 minutes of receipt. |
x-atlas-webhook-key-version | Integer | Which signing key version signed this delivery (useful during rotation). |
Verify x-atlas-webhook-signature, not x-atlas-signature
The canonical
x-atlas-webhook-signature binds the timestamp and delivery id into the signature, so it resists replay. The legacy x-atlas-signature signs only the body and exists for older receivers. New integrations should verify x-atlas-webhook-signature. See Security and signing.x-atlas-signature is deprecated
The body-only
x-atlas-signature header is deprecated. It continues to ship on every delivery so existing receivers keep working, and it will not change or be removed without advance notice in the changelog and a full major-version deprecation window. Do not build new integrations against it, and migrate existing receivers to x-atlas-webhook-signature when you can: it is the only header that defends against replay.Worked example
One complete delivery: the request line, every header with realistic example values, a blank line, then the JSON body.
http
POST /atlas/webhook HTTP/1.1
Host: example.com
content-type: application/json
x-atlas-event: task.completed
x-atlas-delivery: evt_9f2c:0
x-atlas-webhook-id: whd_5a1b2c3d
x-atlas-webhook-timestamp: 1783412096000
x-atlas-webhook-key-version: 1
x-atlas-webhook-signature: 3b8f...e21a
x-atlas-signature: sha256=9c11...77af
{"id":"evt_9f2c...","tenantId":"ten_123","action":"task.completed","target":"task:t_456","actorId":"usr_789","actorKind":"user","context":{},"at":"2026-07-07T12:34:56.000Z"}