Workflow vs Messaging Webhooks
Choose Workflow webhooks, Messaging webhooks, or both. Same token, different jobs.
There is no single product named “workflow webhooks” or “messages webhooks.” Both products expose inbound HTTP (external → NotionApps) and outbound HTTP (NotionApps → external). They look similar in a network log and share one per-app webhook secret, but they are different products with different jobs.
Short answer
Section titled “Short answer”| If you need… | Use |
|---|---|
| A button or form to call Make/Zapier, wait for JSON, then refresh the already-open tab | Workflow: Call webhook and wait |
| An external system to start a specific workflow (create/update records, conditions, Notify) | Workflow: Webhook received |
| That workflow to tell another system “this run finished” (no response needed) | Workflow: Notify → Webhook |
| An event to fan out to several destinations (workflow + partner URL + linked app + screen) | Messaging: inbound webhook and/or webhook subscriber |
| Structured routing (channel, topic, contract, subscribers, request/reply) | Messaging |
| Both process logic and multi-subscriber routing | Both |
Rule of thumb
- Workflow is the process engine: decisions, waits, record writes, emails, and “do this then that.”
- Messaging is the event bus: publish once, deliver to whoever subscribed.
- Call webhook and wait is the only path that can update the same open portal tab after an external system returns JSON. Nothing inbound can do that.
The two products
Section titled “The two products”Workflow Foundation
Section titled “Workflow Foundation”Workflow is a published graph: a trigger starts a run, then steps execute in order (conditions, Notion writes, Notify, waits, UI actions).
Webhooks appear in three Workflow places:
- Trigger: Webhook received — inbound. An external POST starts matching published workflows.
- Step: Notify → channel Webhook — outbound, fire-and-forget. The run POSTs to one or more URLs and continues without using the HTTP response.
- Step: Call webhook and wait — outbound, synchronous. The run POSTs, waits for JSON, stores it on
context.<step_id>, then can Refresh / Open screen / Show message on the open tab.
| Surface | Label | Type |
|---|---|---|
| Trigger | Webhook received | webhook_received |
| Step | Call webhook and wait | call_webhook_and_wait |
| Step | Notify, channel Webhook | send_notification + channel: "webhook" |
Copy the intake URL and cURL from the Workflow builder when the trigger is Webhook received.
Messaging Foundation
Section titled “Messaging Foundation”Messaging is an app-level channel + topic + contract router. Something publishes a message; subscribers receive it.
Webhooks appear in two Messaging places:
- Inbound messaging webhook — an external POST publishes a message. It does not start a workflow by itself. Workflows start only if a subscriber (or a Message received trigger) is wired.
- Subscriber type: Webhook — outbound. When a message matches the subscription, NotionApps POSTs a messaging envelope to the destination URL.
| Surface | Label |
|---|---|
| Builder sidebar | Messaging |
| Subscriber type | Webhook |
| Automation recipes | Send event to webhook; External API/webhook → create record |
Messaging inbound URLs are not copied from the Messaging builder today. Use the shapes in Webhook Technical Reference. Generate the secret in the Workflow builder; the same secret authenticates Workflow and Messaging inbound for that app.
Messaging is not SMS, email delivery, or the in-app inbox. Those are Workflow Notify (email / in-app) or Messaging screens (Conversation, Notification Center, Work Queue). A webhook is just one subscriber type on the bus. See Messaging and Notifications.
All webhook surfaces at a glance
Section titled “All webhook surfaces at a glance”| Surface | Product | Direction | Waits for response? | Starts | Updates open tab? |
|---|---|---|---|---|---|
| Webhook received | Workflow | In | No | Workflow run | No |
| Notify → Webhook | Workflow | Out | No (10s timeout, result unused) | Nothing new; continues the run | No |
| Call webhook and wait | Workflow | Out | Yes (8s inline, then poll up to 45s) | Nothing new; continues the run | Yes, if Live screen updates is on |
| Messaging inbound | Messaging | In | No | A message, then subscribers | No |
| Messaging webhook subscriber | Messaging | Out | No (10s timeout) | Nothing; delivers the message | No |
Legacy sheet automation still has Trigger Webhook / Trigger Integromat (Make)/Zapier. That is the old builder path, not Workflow Foundation.
How they actually differ
Section titled “How they actually differ”1. What a successful inbound “does”
Section titled “1. What a successful inbound “does””Workflow inbound (POST /v1/webhooks/workflow-events or the app alias)
- Matches published workflows whose trigger is Webhook received.
- Success looks like:
{ accepted: true, runs_created, run_ids, intake_event_id }. - The POST body becomes
trigger_payloadfor that run.
Messaging inbound (POST /v1/webhooks/messaging-events or the app alias)
- Requires a
channel(and usually atopic). - Publishes a message on that route.
- Success looks like:
{ accepted: true, message_id, deliveries_created, intake_event_id, deliveries }. - Workflows run only if something subscribed (workflow subscriber or Message received trigger).
Same per-app secret and header (x-notionapps-webhook-token). Different URL, different required body, different success metric.
2. What outbound POSTs look like
Section titled “2. What outbound POSTs look like”Workflow Notify → Webhook (User-Agent: NotionApps-Workflow/1.0)
{ "workflow_id": "...", "workflow_version_id": "...", "run_id": "...", "application_id": "...", "account_id": "...", "subject": "...", "body": "...", "trigger_payload": {}, "context": {}}This is “here is the run that just happened.” Good for Make/Zapier scenarios that need run identity and accumulated step context.
Call webhook and wait
- POSTs either the configured JSON body or, if empty, the full
trigger_payload. - Adds
Idempotency-Key: {run_id}:{step_id}. - Expects Webhook response JSON back (Make’s Webhook response module).
- Stores the parsed body on
context.<this_step_id>(http_status,timed_out, plus response fields such ascart_record_id). - Downstream steps map those fields (Open screen
record_id, conditions, Show message).
Messaging webhook subscriber (User-Agent: NotionApps-Messaging/1.0)
{ "message_id": "...", "message_type": "event", "channel": "integrations", "topic": "record.approved", "contract_name": "...", "contract_version": "...", "conversation_id": "...", "correlation_id": "...", "application_id": "...", "created_at": "...", "payload": {}}payload is full, redacted, or empty (metadata_only) per subscription visibility. There is no run_id / subject / body unless you put them in the message payload yourself.
3. Fan-out vs single run
Section titled “3. Fan-out vs single run”- Workflow is one run, one graph. Multiple outbound URLs mean multiple Notify endpoints (or multiple steps).
- Messaging is one publish, N subscribers. The same approval event can start a workflow, POST a partner webhook, hand off to a linked app, and land in Notification Center without duplicating the publisher.
4. Time, retries, and the open tab
Section titled “4. Time, retries, and the open tab”| Call webhook and wait | Notify webhook | Messaging subscriber | |
|---|---|---|---|
| Inline on button tap | Yes, if Live screen updates is on | No (background) | No |
| Sync wait | Default 8s, max 12s inline | — | — |
| Worker wait | Up to 45s | 10s per URL | 10s |
| Client poll after timeout | 45s (“Working…”) | — | — |
| 4xx/5xx | Fail, no retry, error toast if inline | Retryable queue error | Delivery retry / Activity status |
| Idempotency-Key | Yes | No | Message idempotency_key on ingest |
| Can refresh open tab | Yes | No | No |
Inbound of either kind cannot refresh a tab that is already open. If Make must compute something and the user should see it without a manual refresh, the button must use Call webhook and wait, not “Make calls NotionApps back.”
5. Auth, plans, and monitoring
Section titled “5. Auth, plans, and monitoring”Shared: a per-app webhook secret you generate in the Workflow builder (x-notionapps-webhook-token; also Authorization: Bearer and x-workflow-webhook-token). Copy it once. After this app has an active secret, the old shared platform token stops working for it. How to generate and rotate: Webhook Technical Reference.
Independent:
| Workflow | Messaging | |
|---|---|---|
| Where makers configure it | Workflow builder | Messaging builder + subscriptions |
| Entitlement | Workflow on | Messaging External API for outbound webhook subscribers |
| Kill switches | Workflow Operations → Webhooks | Messaging Operations → Webhooks |
You can disable Messaging outbound without touching Workflow Notify, and the reverse.
6. Gaps that cause the wrong pick
Section titled “6. Gaps that cause the wrong pick”- Workflow inbound URL is in the builder. Messaging inbound URL is not.
- People paste a Workflow intake URL into a Messaging-style partner spec (missing
channel) or the reverse (Workflow body withchannel/topicand no matching workflow). - People use Notify webhook when they needed Call webhook and wait, then wonder why the open tab did not refresh.
- People use Messaging inbound and expect
runs_created— they will getmessage_idinstead.
When to use Workflow only
Section titled “When to use Workflow only”Use Workflow webhooks when the integration is the process.
Webhook received → steps → optional Notify webhook
- Partner POSTs “new order / new ticket / status changed.”
- You need conditions, create/update Notion records, email someone, then optionally ping another system.
- One workflow owns the outcome. You do not need channel/topic routing or multiple subscribers.
- The Webhook intake template is this pattern: receive → check → write record → Notify downstream.
Button / form → Call webhook and wait → Refresh / Open screen / Show message
- User taps Request Revision, Build cart, Price this, or similar.
- Make (or any webhook) must return JSON before the same tab updates.
- Live screen updates must be on for that app.
- Make must end with a Webhook response module.
Button / form → Notify webhook
- User action should tell Slack/Make/a warehouse that something happened.
- You do not need the HTTP response and you do not need the open tab to wait.
Do not use Workflow-only when the same event must go to several independent consumers, or when you want contracts, dry-run, linked-app handoff, or request/reply as the primary model.
When to use Messaging only
Section titled “When to use Messaging only”Use Messaging webhooks when the integration is the event.
Inbound messaging webhook
- External system already thinks in events:
integrations / record.approved. - You want NotionApps to accept the event even if today’s subscribers change tomorrow.
Webhook subscriber
- An app event (form, button publisher, Workflow Publish message, inbound message) should POST to CRM / warehouse / Zapier / a custom API.
- Visibility policy matters (
metadata_only/redacted/full). - You may add a second subscriber later without changing the publisher.
Do not use Messaging-only when the open tab must update from Make’s JSON, when you need a copied intake URL in the maker UI today, or when you need the Workflow Notify envelope (run_id, subject, body, context) as the outbound contract.
When to use both
Section titled “When to use both”They are designed to stack. Shared token, separate policies.
Pattern A — External event → bus → process + partners
Section titled “Pattern A — External event → bus → process + partners”Partner POST /v1/webhooks/messaging-events → message on integrations / webhook.received → workflow subscriber: create/update record, email, conditions → webhook subscriber: POST partner/warehouse → optional screen / linked-app subscriberUse when the inbound event is a domain event, not “start this one workflow.”
Pattern B — Process first, then publish for routing
Section titled “Pattern B — Process first, then publish for routing”Workflow (button, form, or Webhook received) → writes / conditions / Notify email → Publish message → Messaging routes → webhook subscriber(s) → linked app → Notification CenterUse when Workflow owns the decision, but several systems should hear the result.
Pattern C — Workflow inbound + Workflow Notify (no Messaging)
Section titled “Pattern C — Workflow inbound + Workflow Notify (no Messaging)”POST /v1/webhooks/workflow-events → Webhook received workflow → condition / create record → Notify → WebhookThis is both directions of Workflow, not “both products.” Fastest partner demo. No subscribers or linked-app fan-out.
Pattern D — Make-then-refresh, plus a bus event
Section titled “Pattern D — Make-then-refresh, plus a bus event”Button → Call webhook and wait (Make returns JSON, tab refreshes) → Publish message (optional) → webhook subscriber for audit/warehouseCall webhook and wait is for the user. Messaging (or Notify) is for everyone else.
Pattern E — Automation recipe “External API/webhook → create record”
Section titled “Pattern E — Automation recipe “External API/webhook → create record””Guided Automation often wires Messaging inbound + contract + workflow subscriber. That is both products even if you only saw one recipe. See Start From a Wizard.
Decision tree
Section titled “Decision tree”Does the already-open tab need to update from an external HTTP response? YES → Workflow: Call webhook and wait (+ Live screen updates ON) (+ Make Webhook response JSON) Optionally also Publish message / Notify if others must hear it.
NO → Is this “start this process” or “publish this event”?
START THIS PROCESS (one workflow owns the work) → Workflow: Webhook received → If another system should be told afterwards: one destination, want run context → Notify → Webhook many destinations / contracts / linked apps → Publish message → Messaging webhook subscriber(s)
PUBLISH THIS EVENT (routing may change later) → Messaging inbound webhook → Add subscribers as needed: process logic → workflow subscriber or Message received trigger partner HTTP → webhook subscriber another app → linked_app subscriber users in-product → Notification Center / ConversationWorked examples
Section titled “Worked examples”1. Approve on the open tab (no Make)
Section titled “1. Approve on the open tab (no Make)”Workflow: Update / Change status → Refresh → Show message. No webhook. Live screen updates on. Messaging only if other apps or a warehouse must hear record.approved.
2. Request Revision via Make, then stay on the same screen
Section titled “2. Request Revision via Make, then stay on the same screen”Workflow: Call webhook and wait → Refresh or Open screen. Not Notify webhook. Not Messaging inbound. Not “Make POSTs back to NotionApps.”
3. Partner sends “payment succeeded”
Section titled “3. Partner sends “payment succeeded””Workflow-only: Webhook received → create payment row → Notify email → optional Notify webhook.
Messaging (better if more consumers are coming): inbound on integrations / payment.succeeded → workflow subscriber writes the row → webhook subscriber to finance → later a linked-app subscriber without changing the partner.
4. Form submit should email a manager and ping Slack
Section titled “4. Form submit should email a manager and ping Slack”Simple: Workflow on Form submitted → Notify email → Notify webhook.
Routable: Workflow Publish message → webhook subscriber does Slack. Use this if Slack is one of several listeners.
5. Two apps, one approval
Section titled “5. Two apps, one approval”Messaging: publish on a contract; linked_app subscriber plus optional webhook subscriber for a non-NotionApps system. Workflow in each app handles local writes. See Linked App Exchange.
Inbound request shapes (do not mix)
Section titled “Inbound request shapes (do not mix)”Workflow inbound
Section titled “Workflow inbound”POST {API}/v1/webhooks/workflow-eventsPOST {API}/v1/application/{APPLICATION_ID}/workflows/webhook-events{ "application_id": "APP_ID", "target_type": "application", "event_id": "evt_001", "idempotency_key": "partner:evt_001", "payload": { "status": "approved", "name": "Jordan Lee" }}Messaging inbound
Section titled “Messaging inbound”POST {API}/v1/webhooks/messaging-eventsPOST {API}/v1/application/{APPLICATION_ID}/messaging/webhook-events{ "application_id": "APP_ID", "channel": "integrations", "topic": "webhook.received", "event_id": "msg_evt_001", "idempotency_key": "msg_idem_001", "payload": { "external_id": "evt_1001", "status": "approved" }}Production API host is https://api.notionapps.com unless support tells you otherwise. Do not mix staging tokens with production URLs. Full curl: Webhook Technical Reference.
What each cannot do
Section titled “What each cannot do”| Need | Workflow inbound | Notify webhook | Call webhook and wait | Messaging inbound | Messaging subscriber |
|---|---|---|---|---|---|
| Start a specific workflow from a partner POST | Yes | — | — | Only via subscriber | — |
| Fan-out to N destinations from one event | No | Multiple URLs on one step only | No | Yes | Yes (as one of N) |
| Use Make’s JSON on the open tab | No | No | Yes | No | No |
Return run_id / step context to a catcher |
— | Yes | Custom body only | — | Only if you put it in payload |
| Contracts / dry-run / linked apps | No | No | No | Yes | Yes |
| Copy intake URL in maker UI | Yes | — | — | Not today | — |
Simulator / builder preview does not prove live HTTP. Use a real inbound curl and an outbound catcher on the target environment.
Common mistakes
Section titled “Common mistakes”- Treating Workflow and Messaging as two phases of one webhook. They are two products. A partner can call either URL; the wrong one “succeeds” with a different JSON shape and no visible workflow.
- Notify webhook for Make-then-refresh. Notify does not wait and cannot refresh the open tab. Use Call webhook and wait; Make must respond with Webhook response JSON.
- Inbound Make → NotionApps to update the tab. Inbound starts new work in the background. The tab that already sent the button tap will not refresh from that.
- Messaging inbound, looking for
runs_created. You will seemessage_id. Ifdeliveries_createdis 0, no subscriber matched — the workflow never ran. - Workflow inbound body with only
channel/topic. Workflow matching uses application/target/conditions, not Messaging routes. - Assuming one kill switch. Workflow outbound off does not stop Messaging subscribers, and the reverse.
- Missing External API. Messaging webhook delivery needs Messaging External API. Workflow Notify / Call webhook and wait do not use that same gate.
- Using email, SMS, or Conversation screens as “message webhooks.” Unrelated. Conversation is an in-app screen.
Builder checklist
Section titled “Builder checklist”- Workflow is published (drafts can accept HTTP and create zero runs).
- Use a new
event_id/idempotency_keywhen retesting. - Call webhook and wait has a URL; Make returns JSON via Webhook response.
- Messaging subscriber destination is a real
https://URL. - Messaging outbound needs External API entitlement.
Related
Section titled “Related”- Webhooks and Integrations — Call webhook and wait setup
- Webhook Technical Reference — URLs, tokens, curl
- Messaging and Notifications — channels, topics, subscribers
- Application Messaging Foundation Guide — contracts and External API
- Workflow Foundation User Guide — steps and runs
- Step catalog —
call_webhook_and_wait,send_notification,publish_message - 28 Aug 2026 Release — live screen updates
- How does messaging work with workflows?