Events and webhooks
What changes, in order, and signed notices to your systems.
Updated on January 1, 1970
Privacy and analytics
We use necessary technical cookies and, only with your consent, analytics and marketing pixels to improve the website and measure campaigns.
What changes, in order, and signed notices to your systems.
Updated on January 1, 1970
Every change made in Nömad (through the screens, the API, the MCP or Nödo) leaves an ordered event: <entity>.<verb>, who did it, where and which records it touched (their ids and the names of the fields, never their values). Read events with events.list from a cursor, or receive them in your system with a signed webhook. The log keeps 30 days.
{
"id": "1042",
"object": "event",
"type": "works.tasks.update",
"schema_version": 1,
"operation": "works.tasks.update",
"plane": "client",
"occurred_at": "2026-10-26T12:00:00Z",
"actor": {
"kind": "person",
"id": "…",
"client": null
},
"door": "web",
"scope": "brand",
"brand_id": "…",
"workspace_id": "…",
"data": {
"object_ids": [
"…"
],
"changed_fields": [
"title",
"status"
]
},
"request_id": "…"
}You only see events of what you can read, with the same keys as the records. For the data, read the record with its operation.
events.list with after = the last id you read; next_cursor is the next one. It advances even when there is nothing you can see.from_now: true returns the current cursor: take it before copying the state and follow from it.cursor_expired: your cursor is older than the log. Copy the state again.workspace.webhooks.create): a public HTTPS address and the types: *, an exact one or a prefix works.*. You can limit it to some brands.whsec_…) is shown once. Rotating keeps the previous one signing for 24 h.Nomad-Signature: t=<unix>,v1=<hex>: HMAC-SHA256 of <t>.<body>. Reject signatures older than 300 s. Also Nomad-Event-Id, Nomad-Event-Type, Nomad-Delivery-Id and Nomad-Delivery-Attempt.Nomad-Event-Id to avoid processing the same event twice: a retry or a replay repeats it.import { createHmac, timingSafeEqual } from 'node:crypto'
// header = request.headers['nomad-signature'], body = the raw request body
export function verify(body, header, secret, now = Math.floor(Date.now() / 1000)) {
const pairs = header.split(',').map((part) => part.split('='))
const t = Number(pairs.find(([key]) => key === 't')?.[1])
if (!t || Math.abs(now - t) > 300) return false
const expected = createHmac('sha256', secret).update(`${t}.${body}`).digest()
return pairs.filter(([key]) => key === 'v1').some(([, value]) => {
const given = Buffer.from(value, 'hex')
return given.length === expected.length && timingSafeEqual(given, expected)
})
}