Skip to content

Write a webhook

See Webhooks for the full field reference and delivery model this guide only walks through the common path of.

Targets live in a YAML file, not a database table — this can hold real bearer tokens and HMAC secrets, so it belongs in a file kept out of git, not a row a superuser edits at runtime:

Terminal window
cp api/webhooks.example.yaml webhooks.yaml

Edit webhooks.yaml with the one endpoint you want to notify first. This example fires on indexing or upload, only for the invoices bucket:

targets:
- name: my-first-target
url: https://example.com/hooks/atlas
events: [INDEX, UPLOAD]
bucket: invoices
secret: a-shared-secret-only-this-target-and-atlas-know
body:
bucket: ${bucket}
path: ${path}
object_id: ${object_id}

Omit events to fire on every action, and bucket to fire for every bucket. secret is optional but recommended — it’s what lets the receiver verify a delivery actually came from Atlas (step 4).

Terminal window
ATLAS_WEBHOOKS_CONFIG_PATH=./webhooks.yaml

This is read once at process start — restart the API after editing either the path or the YAML file itself.

Trigger the action you configured (declare a document, upload a file, …) and check your endpoint received a signed POST:

POST /hooks/atlas HTTP/1.1
Content-Type: application/json
X-Atlas-Signature: sha256=5f1c2e...a91b
{"bucket": "invoices", "path": "2024/invoice_042.pdf", "object_id": "0198e2b4-..."}

On the receiving end, confirm the signature before trusting the payload:

import hashlib
import hmac
def verify(secret: str, signature_header: str, raw_body: bytes) -> bool:
algo, _, signature = signature_header.partition("=")
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return algo == "sha256" and hmac.compare_digest(expected, signature)

5. Add more targets, or more ${variable} substitution

Section titled “5. Add more targets, or more ${variable} substitution”

Every string in url, headers, and body — at any nesting depth — supports the same substitution. The full variable list (${event_id}, ${actor}, ${state}, ${context_json}, …) and every optional field (method, headers, skip_tls_verification, …) are in Webhooks.