Webhook Technical Reference
Canonical maker page for inbound and outbound HTTP. Not sure which product? Start with Workflow vs Messaging Webhooks. Admin kill switches, host env files, and internal endpoints stay in repo docs/ — not here.
What this is / when to use it
Section titled “What this is / when to use it”| Direction | Meaning | NotionApps surface |
|---|---|---|
| Inbound | An external tool POSTs JSON into NotionApps and starts work | Workflow trigger webhook_received, or Messaging inbound that publishes a message |
| Outbound | NotionApps POSTs JSON to a URL you own | Notify (fire-and-forget), Call webhook and wait (sync JSON), or a Messaging subscriber of type Webhook |
Use webhooks when another system is the first place work begins (CRM, payments, a form tool), when a downstream system must hear the result, or when a button must wait for Make and then refresh the already-open tab.
When not to: you only need email. Use send_notification. You are still exploring — start with the wizard, not HTTP.
Before you start
Section titled “Before you start”- Workflow entitlement is on (Messaging External API level if you use Messaging webhooks).
- You have the application id from the builder URL.
- You can generate a webhook secret in the workflow builder when the trigger is Webhook received. Copy it once — it is not shown again.
- You have an outbound catcher (webhook.site, Hookdeck, Zapier Catch Hook) if you will test outbound.
- The workflow is published / active. Drafts can accept HTTP and create zero runs.
Create a webhook secret
Section titled “Create a webhook secret”The secret belongs to this app. Workflow inbound and Messaging inbound both use it.
- Open Automation → Workflows.
- Select (or create) a workflow whose trigger is Webhook received.
- Open External webhook intake.
- Enter a name you will recognize later, for example
Make production. - Choose an active term: 30, 90, 180, or 365 days, or No expiry.
- Click Generate secret.
- Copy the value immediately. Paste it into Make, Zapier, or your sender as:
x-notionapps-webhook-token: {PASTE_THE_SECRET}The builder also accepts Authorization: Bearer {SECRET} and x-workflow-webhook-token: {SECRET}.
The list then shows only a prefix (for example na_wh_8f3a2c1b), the name, status, days left, and last used. The full secret is never shown again.
Rotate
- Generate a new secret (you can have up to five active secrets).
- Update Make, Zapier, or the sender to the new value.
- Confirm a test POST succeeds.
- Revoke the old secret.
You cannot revive a revoked secret. Generate a new one instead.
Rules
- A secret for App A cannot post into App B.
- Revoked or expired secrets return 401.
- If inbound webhooks are disabled for the app, generate is blocked.
- Do not put the secret in a Notion property that guests can open.
Build it
Section titled “Build it”Webhook intake template (recommended first route)
Automation → Workflows → Start from a template → Webhook intake, or recreate:
| Order | Step | Purpose |
|---|---|---|
| 1 | Condition | e.g. payload.status equals approved |
| 2 | Create record | Map payload.name, payload.email |
| 3 | Notify (channel = Webhook) | Outbound proof to your catcher |
| 4 | End | Done |
Trigger: webhook_received on the application.
Replace example field names with your Notion properties. Full step list: Step catalog.
Inbound URL shapes
POST {API_HOST}/v1/webhooks/workflow-eventsPOST {API_HOST}/v1/application/{APPLICATION_ID}/workflows/webhook-events
POST {API_HOST}/v1/webhooks/messaging-eventsPOST {API_HOST}/v1/application/{APPLICATION_ID}/messaging/webhook-events{API_HOST} is the API for your environment (production https://api.notionapps.com unless support tells you otherwise). Prefer the app alias URL from the builder so the event is already scoped.
Auth (maker)
Use the secret you generated above. Do not guess a platform token and do not reuse a secret from another app.
If a request returns 401/403, the secret is wrong, revoked, expired, or bound to a different app. If it returns 503 about a token not configured, that is an environment problem — contact support.
Test inbound
- Publish the workflow.
- POST JSON with a unique
event_id/idempotency_keyevery try (retries can look like “nothing happened”). - Example body:
{ "application_id": "YOUR_APP_ID", "event_id": "test-2026-08-31-001", "payload": { "status": "approved", "name": "Sample Lead", "email": "user@example.com" }}- Expect an accepted response and
runs_created >= 1when the trigger matched. - Confirm the Notion row and, if you added Notify, the catcher POST.
Test outbound
- Notify step → Channel Webhook → paste the catcher URL.
- Subject/body you will recognize.
- Fire inbound (or Simulate if Simulate sends outbound in your build).
- Confirm the catcher shows headers + body.
Call webhook and wait (open-tab refresh)
Use Call webhook and wait when a button must POST Make (or another webhook), receive JSON, then Refresh or Open screen on the same open tab. This is not fire-and-forget Notify webhook.
- Add the step and paste the Make webhook URL.
- Leave the sync timeout at 8000 ms unless you need a shorter wait (max 12 seconds on the tap).
- In Make, end with Webhook response and return JSON, for example
{ "ok": true, "cart_record_id": "…" }. - Map a later Open screen record id to
context.<this_step_id>.cart_record_idwhen Make returns one. - Follow with Refresh / Open screen / Show message.
What the user sees:
- Make answers within 8 seconds: the following UI steps run on the open tab.
- Make is still going: Working… then the app polls the run for 45 seconds.
- Still pending: “This is taking longer. Refresh to see the cart.”
- 4xx / 5xx in the sync window: error, no refresh, no retry.
Live screen updates must be on for the app, or Refresh after this step will not update the open tab. The switch is off by default. NotionApps turns it on from Workflow Operations → Controls.
Inbound webhooks Make fires back at NotionApps still cannot refresh an already-open tab.
Every control
Section titled “Every control”| Control | Where | What it does |
|---|---|---|
| Trigger Webhook received | Workflow | Starts a run from HTTP. |
| Intake URL (central / app alias) | Builder | Where senders POST. |
| Generate secret | External webhook intake | Creates a per-app secret. Plaintext is shown once. |
| Name + active term | External webhook intake | Label and 30 / 90 / 180 / 365 days or no expiry. |
| Revoke | External webhook intake | Immediately rejects that secret. |
| Token header | Sender (Make / Zapier) | x-notionapps-webhook-token. |
Condition on payload.* |
Step | Gate. |
| Create/update record maps | Step | payload.field → Notion. |
| Notify channel Webhook | Step | Fire-and-forget outbound. |
| Call webhook and wait | Step | Sync POST; waits for JSON Webhook response. |
| Messaging inbound / subscriber | Messaging | Separate product; needs External API level. Uses the same app secret. |
| Idempotency key | Payload | Dedupes retries. |
What users see
Section titled “What users see”Inbound and fire-and-forget Notify usually show nothing to the current tab. Call webhook and wait can Refresh / Open screen / Show message on the open tab when Live screen updates is on. Operators may also see a Work Queue item or Notification if you added those steps. An inbound sender sees only the HTTP status.
Limits and plans
Section titled “Limits and plans”- Messaging webhooks need External API. Workflow Notify webhooks and Call webhook and wait need Workflow on, not Messaging levels.
- Up to five active secrets per app. Revoke or expire one before generating another if you are at the cap.
- After the first active secret, the shared platform token no longer works for that app.
- Call webhook and wait needs a reachable HTTPS URL. Make must return JSON via Webhook response.
- Unpublished workflows: HTTP may return accepted with
runs_created: 0. - Payload size and rate limits follow the API. Keep test bodies small.
- Do not put secrets in Notion properties that guests can open.
Example
Section titled “Example”You generate a secret named Make production with a 90-day term. Make stores x-notionapps-webhook-token and POSTs { status, name, email } to the app alias URL. Condition requires approved. Create record on Leads. Notify the outbound catcher. The sample lead appears in the app list; the catcher logs the outbound POST.
Fix problems
Section titled “Fix problems”| Symptom | Likely cause | What to do |
|---|---|---|
| 401 | Wrong, revoked, or expired secret, or a secret from another app | Generate a new secret if you lost the value. Confirm the prefix matches this app. Stay on one environment. |
| 401 after you generated a secret | Sender still uses the old shared platform token | Update Make/Zapier to the new secret. |
| Generate is blocked | Inbound webhooks are disabled for the app | Ask NotionApps to turn inbound back on. |
| At the 5-secret cap | Too many active secrets | Revoke or expire one, then generate. |
| Accepted, 0 runs | Draft, or trigger not webhook_received | Publish. Check trigger. |
| Condition failed | payload.status mismatch |
Log the raw body. Fix the path. |
| Outbound missing | Notify not Webhook, or URL wrong | Catcher URL. Publish. |
| Open tab stale after Make | Used Notify, or Make has no Webhook response, or live screen updates is off | Use Call webhook and wait. End Make with Webhook response JSON. Ask NotionApps to turn the switch on. |
| Messaging 403 | Level below External API | Request access. |
Related
Section titled “Related”Workflow vs Messaging Webhooks. 31 Aug 2026 Release. Trigger catalog. Which automation.