Skip to content
newThe webhook.co MCP server is live — turn any webhook into an agent event. Read the docs
test locally

Test Notion webhooks locally

Notion's webhooks open with a handshake, not an event: nothing is delivered until you read a one-time verification token off a live request and paste it back into the dashboard. The docs are also blunt that localhost won't do.

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.

wbhknotion
$ wbhk endpoints create notion-dev
https://wbhk.my/whep_…
$ wbhk listen <endpoint-id> --forward http://localhost:3000/webhooks
$ wbhk replay <event-id> --forward http://localhost:3000/webhooks

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.

Notion webhooks: common questions

Does Notion have a test webhook button?

No test-send affordance appears anywhere in Notion's webhook documentation. The only thing the dashboard will re-fire is the one-time verification token, via Resend token. To produce a real event you perform a real action in the workspace, such as adding a comment or renaming a page.

Why is my Notion webhook subscription not delivering anything?

A new subscription stays pending verification until you paste the verification token back into the Webhooks tab and confirm. Until then Notion sends nothing beyond that single handshake request. Later on, repeated delivery failures can inactivate a working subscription as well.

Can I point a Notion webhook at localhost?

No. Notion requires a secure, publicly available endpoint and says outright that endpoints in localhost are not reachable. You need a public HTTPS URL that Notion can reach, both for the initial verification request and for every event after it.

How does Notion sign its webhook requests?

Every request carries an X-Notion-Signature header: an HMAC-SHA256 of the request body, keyed with the verification token from the initial handshake, hex-encoded and prefixed with sha256 followed by an equals sign. Save the token when you verify, because it is the only way to check signatures.

Can I create a Notion webhook subscription through the API?

Notion documents subscription management only through the connection settings UI, and no webhook subscription endpoints appear in its API reference. Plan on creating, verifying and deleting subscriptions by hand in the Webhooks tab, including whenever you need to repoint a URL.

How quickly does Notion deliver a webhook event?

Notion says events should be delivered within five minutes of occurring, and most within a minute. Aggregated event types add a further delay the docs describe as typically under one minute, which is why the troubleshooting advice favours non-aggregated types when testing.

Point a webhook at it. Watch it land.

Start free: a permanent URL, full inspection, one-command replay — and outbound delivery, because every feature is on every plan. Move up when you need more events.