Webhooks
A webhook notifies another system, yours or a tool such as n8n, Make or Zapier, the moment something happens in Magnitude: a ticket opens, changes, gets resolved. You have nothing to poll: the event is delivered to you, signed, at the address you gave.
Declaring an endpoint
In Settings, Webhooks, add an HTTPS address (a plain-text address is refused: a secret and customer data do not travel over an unencrypted channel) and tick the events you care about. On creation, the product shows you the endpoint's secret once. Copy it into your system: it will never be shown again, and it is what lets you verify that what you receive really comes from us.
The events
| Event | When it fires |
|---|---|
ticket.created | A ticket is opened: by hand, by the API, by the AI agent, by the website cart |
ticket.updated | A ticket changes: priority, state, team, assignee, fields |
ticket.resolved | A ticket is marked resolved |
ticket.closed | A ticket is closed |
cart.submitted | The website cart is sent (button or agent): selection, quantities, references, contact, ticket number, exit used |
Other names exist in the list (conversation.*, contact.*, message.*): they are reserved and do not fire yet. Do not build on them today; this page will say the day they do.
What you receive
A POST request, JSON body, with three headers:
| Header | Content |
|---|---|
webhook-id | The event's identifier. The same event replayed carries the same identifier: ignore an identifier you have already seen |
webhook-timestamp | The moment of sending, in seconds |
webhook-signature | v1, followed by the signature (see below) |
The body:
{
"id": "a01ae3e8-…",
"type": "ticket.created",
"data": {
"id": "04163729-…", "number": 11,
"subject": "Exchange of a mountain bike delivered with a scratched frame",
"description": "…",
"status": "open", "status_label": null, "priority": "high",
"type_id": "fc0c1209-…",
"custom": { "product": "mtb", "order_number": "TB-7788" },
"origin": "ai", "channel": "website",
"requester_contact_id": "e645f0…", "conversation_id": null,
"assignee_user_id": null, "team_id": null,
"resolution_code": null, "cause_code": null,
"created_at": "2026-09-03T01:10:17.212Z", "updated_at": "2026-09-03T01:10:17.212Z"
}
}custom carries the type's business fields, by their key (the one you see in Settings, Ticket types). origin says who opened the ticket: agent (a member), ai (the AI agent), customer (a customer, for example the website cart), api or external (another system).
Verifying the signature
The signature is an HMAC SHA-256, with your secret, of the string identifier.timestamp.body (the three values separated by a dot, the body exactly as received, byte for byte), base64-encoded. Recompute it and compare: if it differs, ignore the request. This is the Standard Webhooks convention, which most libraries can verify as is.
What happens if your system does not answer
Any response other than 2xx, or no response, is a failure. The product retries, with growing waits: right away, then 1 minute, 5 minutes, 30 minutes, 2 hours, 10 hours. After twenty failures in a row, the endpoint disables itself: a queue that endlessly retries a dead endpoint becomes the outage. You re-enable it in Settings, Webhooks, once your system is repaired.
Try it in five minutes
- Open a test address: n8n (a Webhook node in test mode), or a service such as webhook.site, gives you an HTTPS address that displays what it receives.
- Declare it in Settings, Webhooks, with
ticket.createdandticket.updated. - Open a ticket (by hand, from a conversation, or by asking your website agent for one), then change its priority.
- Watch the two requests arrive, with their headers.
What does not exist yet
- Conversation, contact and message events do not fire (see above).
- No replay on demand: an event missed during an outage on your side is retried on the schedule above, no more.
- No filter by ticket type or by team: an endpoint receives every ticket of the workspace for the events it has ticked.