Webhooks#
Warning
Webhooks are an experimental feature. The API and configuration format may change in future releases.
Webhooks let Tiled notify an external HTTP service whenever a catalog event occurs — for example, when a new entry is created or a stream is closed. This is useful for triggering downstream pipelines, sending notifications, or integrating Tiled with other systems without polling.
Enabling webhooks#
Webhooks are disabled by default. Add a webhooks: section to your
server configuration file to turn them on:
webhooks:
secret_keys:
- ${TILED_WEBHOOK_SECRET_KEY}
secret_keys is required. Generate a key with:
openssl rand -hex 32
Registering a webhook#
Webhooks are registered against a node path. Any event on that node — or any of its descendants — will be delivered.
curl -X POST https://my-tiled-server/api/v1/webhooks/target/my/container \
-H "Authorization: Apikey $TILED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://my-service.example.com/hooks/tiled",
"secret": "my-hmac-secret",
"events": ["container-child-created", "stream-closed"]
}'
Field |
Required |
Description |
|---|---|---|
|
yes |
HTTPS endpoint that will receive |
|
no |
HMAC signing secret; requires |
|
no |
List of event types to deliver; set |
Registering a webhook requires the write:webhooks scope (admin only).
The webhook URL must use HTTPS.
Event types#
Type |
When it fires |
|---|---|
|
A new entry is created inside the watched container |
|
Metadata on a child entry is updated |
|
A live-stream entry is closed |
Each delivery is a JSON POST with a body like:
{
"type": "container-child-created",
"timestamp": "2025-01-15T12:34:56.789Z",
"key": "scan_001",
"path": ["my", "container"],
"structure_family": "array",
"specs": [],
"metadata": {}
}
Verifying deliveries#
When a secret is set, Tiled adds an X-Tiled-Signature header to every
request:
X-Tiled-Signature: sha256=<hex-digest>
Verify it in your receiver:
import hashlib, hmac
def verify(body: bytes, secret: str, header: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header)
Each delivery also carries an X-Tiled-Event-ID header — a unique hex string
you can use to deduplicate retried deliveries, for example:
seen = set()
def handle(request):
event_id = request.headers["X-Tiled-Event-ID"]
if event_id in seen:
return # duplicate delivery, ignore
seen.add(event_id)
...
Managing webhooks#
List webhooks registered on a node:
curl https://my-tiled-server/api/v1/webhooks/target/my/container \
-H "Authorization: Apikey $TILED_API_KEY"
Delete a webhook (use the id returned when you registered it):
curl -X DELETE https://my-tiled-server/api/v1/webhooks/42 \
-H "Authorization: Apikey $TILED_API_KEY"
View delivery history for a webhook:
curl "https://my-tiled-server/api/v1/webhooks/history/42?limit=20" \
-H "Authorization: Apikey $TILED_API_KEY"
History records are retained for 30 days and then pruned automatically.
Retry behaviour#
If the target URL returns a non-2xx response or is unreachable, Tiled retries
up to 3 times with exponential back-off (roughly 1 s → 5 s → 25 s, plus
random jitter). The final outcome (success or failed) is recorded in the
delivery history.
Security notes#
Webhook URLs must use HTTPS.
Registering and listing webhooks requires the
write:webhooks/read:webhooksscope, which is granted to admin users only. This is the primary access-control layer preventing unauthorized webhook registration.At registration time, Tiled additionally checks that the target URL does not resolve to a private, loopback, or link-local address (SSRF protection). Note that this hostname-level check can be bypassed by DNS-rebinding attacks; for high-security deployments also route outbound webhook requests through a network-level egress proxy (e.g. Smokescreen).
HMAC signing secrets are encrypted at rest using the server’s
secret_keys.