The loop
Create an endpoint and it prints a permanent ingest URL. Everything sent there is captured whole — headers, body, the exact bytes — before anything else happens. Stream those events to a port on your machine, and replay any one of them as often as you like while you fix the handler. Forwarding runs from your own machine, so it isn't a billable delivery.
What's different about Notion
Nearly all the friction with Notion is front-loaded. A subscription does not start delivering when you save it. Notion immediately POSTs a one-time body containing a verification_token to the URL you entered, and you have to read that value out of the request and paste it back into the Webhooks tab before a single event flows. The same token then doubles as the HMAC-SHA256 key for the X-Notion-Signature header on every later delivery. Which means you need a reachable, inspectable URL before you have anything worth inspecting.
Where the URL goes
Open your connection settings at app.notion.com/developers/connections, create or pick a connection, then go to the Webhooks tab and click "+ Create a subscription". Enter the public webhook URL, choose the event types you want, and click "Create subscription". The subscription exists at that point but is not yet verified: catch the verification_token Notion posts to it, paste the value into the form, and click "Verify subscription".
There is no test-send affordance anywhere in Notion's webhook reference. The only thing the dashboard will re-fire is the one-time verification token, via "Resend token". Notion's own testing section tells you to go and do the thing: change a page title, add a comment, modify a database schema. For quick feedback the docs suggest starting with non-aggregated events like comment.created or page.locked.
What Notion sends
Event types are dot-separated and usually read entity.action — entity being page, database, data_source, comment, file_upload or view, and action in snake_case. Usually, not always: page.transcription_block.transcript_deleted carries three segments. The data_source family arrived with the 2025-09-03 API version, which also deprecated database.content_updated and database.schema_updated.
- page.created
- page.content_updated
- page.properties_updated
- comment.created
- data_source.schema_updated
- page.deleted
Worth knowing first
The verification token is also the signing key
Notion sends verification_token exactly once, in the body of the POST that lands the moment you create the subscription. Pasting it back into the Webhooks tab is what turns delivery on — but keep the value afterwards. X-Notion-Signature on every subsequent request is an HMAC-SHA256 of the request body keyed with that token, hex-encoded and prefixed with sha256=.
Localhost is not an option
Notion requires "a secure (SSL) and publicly available endpoint" and states plainly that "Endpoints in localhost are not reachable". There is no development exception. Whatever URL you register has to answer from the public internet, first for the verification POST and then for every event that follows it.
The URL is frozen after verification
You can change a subscription's webhook URL only before it is verified. Afterwards, repointing it means deleting the subscription and creating a new one, which means a fresh verification token and fresh plumbing on your side. Subscribed event types, by contrast, can be changed at any time.
Events are signals, not content
Notion is explicit that the events do not contain the full content that changed. The payload tells you which entity moved and when; retrieving what it now says is a follow-up call to the Notion API. Common fields include type, entity, data, id, timestamp, workspace_id, workspace_name, subscription_id, integration_id, authors, attempt_number and api_version.
Aggregation can swallow your test
High-frequency events such as page.content_updated are aggregated per entity, adding a delay Notion describes as typically under one minute. Events in quick succession may collapse into only the most meaningful result event — or none at all, if the state ends up back where it started. Order is not preserved either; reorder on the event's timestamp field.
At-most-once delivery, then inactivation
Notion aims for at-most-once delivery and retries up to 8 times on an exponential backoff, the final attempt landing roughly 24 hours after the event; attempt_number on each delivery runs 1 to 8. Repeated failures can inactivate a subscription outright — the compliance audit log documents both a failing and a restored event for exactly that.
Signature checking is handled for you once the secret is registered — you can also check a captured signature by hand in the signature verifier.