Notion’s webhooks are signal-only: the payload tells you that something changed (a page id, a list of property ids) but never the new values. Every event forces a follow-up GET, and those follow-ups burn the same 3 requests/second budget your automation is already rationing. Here is the failure mode in detail, and what a payload should carry instead.
A typical delivery for a property change looks like this (July 2026, per Notion’s webhook reference):
{
"type": "page.properties_updated",
"entity": { "type": "page", "id": "<page_uuid>" },
"data": {
"parent": { "type": "data_source_id", "data_source_id": "..." },
"updated_properties": ["<prop_id_1>", "<prop_id_2>"]
}
}updated_properties is a list of property ids. There is no old value, new value, card title, or column name. To learn what happened you call the API again. A few more contract details that matter when you build on it:
Now put those properties together on a busy board. A batch step moves 50 cards. You receive ~50 webhooks that each say “something changed on page X.” Your handler dutifully issues 50 follow-up reads, plus block fetches for the cards with content, straight into a 3 req/s limiter. The queue backs up, some reads 429, your retries collide with the next batch’s events, and if a delivery drops mid-storm there is no replay to recover it.
The perverse part: the busier your board, the more your notifications consume your API budget. Teams end up standing up a caching proxy just to answer “what changed?” That proxy is infrastructure whose only job is to work around the payload shape.
NovumOS events carry the change inline. The same card move looks like this, and the envelope shape is a stable, versioned contract that won’t shift under your integration:
{
"object": "event",
"event_id": "1d4f3c2a-7a06-4b3a-b3f0-2e9a47b5d1c3",
"event_type": "card.moved",
"delivered_at": "2026-05-12T16:00:01.123456Z",
"entity": { "type": "page", "id": "0192a4b2-..." },
"data": {
"card_id": "0192a4b2-...",
"board_id": "0192a4b2-...",
"column_id": "0192a4b2-...",
"from_column_id": "0192a4b2-...",
"to_column_id": "0192a4b2-..."
}
}data: a move carries from/to columns; an update carries a diff map of the fields that changed. Most consumers never make a follow-up call.event_id and a retry can never double-apply. Failed deliveries retry on a backoff schedule, land in a dead-letter queue, and can be replayed with the same event_id.The pattern that suffers least on sparse payloads: subscribe to the narrowest event types available, dedupe aggregated deliveries before fetching, keep a local last-known-state cache so unchanged fields don’t trigger reads, and give webhook handlers their own slice of the rate budget so a notification storm can’t starve your writes.
Point your webhook receiver at a board that sends the diff. The free tier needs no card and includes webhooks.
Create account