Skip to content

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.

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.

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:

  1. Trigger: Webhook received — inbound. An external POST starts matching published workflows.
  2. Step: Notify → channel Webhook — outbound, fire-and-forget. The run POSTs to one or more URLs and continues without using the HTTP response.
  3. 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 is an app-level channel + topic + contract router. Something publishes a message; subscribers receive it.

Webhooks appear in two Messaging places:

  1. 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.
  2. 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.

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.

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_payload for that run.

Messaging inbound (POST /v1/webhooks/messaging-events or the app alias)

  • Requires a channel (and usually a topic).
  • 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.

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 as cart_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.

  • 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.
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.”

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.

  • 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 with channel/topic and 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 get message_id instead.

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.

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.

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 subscriber

Use 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 Center

Use 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 → Webhook

This 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/warehouse

Call 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.

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 / Conversation

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.”

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.

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.

POST {API}/v1/webhooks/workflow-events
POST {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"
}
}
POST {API}/v1/webhooks/messaging-events
POST {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.

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.

  1. 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.
  2. 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.
  3. 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.
  4. Messaging inbound, looking for runs_created. You will see message_id. If deliveries_created is 0, no subscriber matched — the workflow never ran.
  5. Workflow inbound body with only channel / topic. Workflow matching uses application/target/conditions, not Messaging routes.
  6. Assuming one kill switch. Workflow outbound off does not stop Messaging subscribers, and the reverse.
  7. Missing External API. Messaging webhook delivery needs Messaging External API. Workflow Notify / Call webhook and wait do not use that same gate.
  8. Using email, SMS, or Conversation screens as “message webhooks.” Unrelated. Conversation is an in-app screen.
  • Workflow is published (drafts can accept HTTP and create zero runs).
  • Use a new event_id / idempotency_key when 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.