Receiving Webhooks
When something happens in your team, we record an event for every active webhook subscribed to it and send it
to the webhook's url shortly after, in the background.
The request
By default, events are sent with POST and a JSON:API document as the body, in the same format as our other
API responses: a resource of type event whose object relationship points to the resource the event is about
(sideloaded in included) and whose team relationship points to your team. The
Webhook Events pages describe each event and its payload. Fields that the REST API only returns for particular OAuth scopes are omitted from
payloads.
{
"data": {
"id": "1377451",
"type": "event",
"attributes": {
"name": "asset_created",
"created_at": "2025-03-14T16:42:07.000Z"
},
"relationships": {
"object": { "data": { "type": "asset", "id": "5501" } },
"team": { "data": { "type": "team", "id": "1" } }
}
},
"included": [
{ "type": "asset", "id": "5501", "attributes": { "name": "Trail boots hero shot" } }
]
}Every request carries these headers:
Content-Type: application/vnd.api+jsonUser-Agent: TINT Webhook/2.0API-Version: the API version the payload was serialized with.X-TINT-Signature: only when the webhook has a signing secret. See Webhook Event Signatures.- The webhook's own
headers, which can override the first three.
A webhook can change all of this: filter limits which events are sent, payload_include and payload_fields
change what the document contains, http_method changes the verb, and payload_transformation_template
replaces the body with whatever the template renders. The payload is rendered once, when the event is recorded,
so every retry sends the same body, and changing a webhook doesn't affect events already recorded.
Responding
Respond quickly with a 2XX status to acknowledge the event, and do any slow work afterwards. Any status below 400 counts as delivered; redirects are not followed. Your response body is ignored, but stored with the attempt in the event log.
- 404 Not Found or 410 Gone: we stop delivering that event immediately and mark it
failure. - Any other 4XX or 5XX status, a timeout or a connection error: we retry the event up to 7 times with an
increasing delay (roughly 1 minute, 1 minute, 2 minutes, 13 minutes, 1 hour, 4 hours and 13 hours after each
failure, plus some random jitter), so about 18 hours in total, then mark it
failure. - Too many failures: once a webhook accumulates more than 50 failed attempts within a week without a
successful delivery in between, we set it to
inactiveand stop recording events for it. Fix your endpoint, then update the webhook withstatus: active. Events that occurred while it was inactive are not sent.
Before every attempt, we also check that the webhook is still active and that its URL still uses https and
resolves to a public IP address; if not, the event is marked failure without being sent.
Duplicates and ordering
- Use the event
id(data.id) to deduplicate. It stays the same across retries, and the same event can occasionally reach you more than once (for example, when your endpoint processed it but responded too late). - Don't rely on the order of events. Events are delivered concurrently and retried independently, so an
asset_updatedcan arrive before the matchingasset_created, and a retried event can arrive after newer ones. Comparecreated_at, or retrieve the current state of the resource from the API, when order matters. - The signature timestamp changes on every attempt (each attempt is signed again), while the body and
data.attributes.created_atdon't. - Events that were never delivered can be listed with the event log and
filter[status]=failure.