> For the complete documentation index, see [llms.txt](https://docs.notionapps.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.notionapps.com/automation/webhooks-and-integrations/workflow-vs-messaging-webhooks.md).

# 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**.

{% hint style="info" %}
**Start here** if you are choosing an integration path. For Make-then-refresh setup, see [Webhooks and Integrations](https://docs.notionapps.com/automation/webhooks-and-integrations). For generating a secret, URLs, and curl, see [Webhook Technical Reference](https://docs.notionapps.com/automation/webhooks-and-integrations/webhook-technical-reference).
{% endhint %}

## 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

### 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:

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 Foundation

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](https://docs.notionapps.com/automation/webhooks-and-integrations/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](https://docs.notionapps.com/automation/messaging-and-notifications).

## 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

### 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_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.

### 2. What outbound POSTs look like

**Workflow Notify → Webhook** (`User-Agent: NotionApps-Workflow/1.0`)

```json
{
  "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`)

```json
{
  "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

* **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

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

{% hint style="warning" %}
**Live screen updates** must be on for the app, or Refresh after Call webhook and wait will not update the open tab. The switch is off by default. NotionApps turns it on from Workflow Operations → Controls. See the [28 Aug 2026 Release](https://docs.notionapps.com/release-notes/28-aug-2026-release) and the [Step catalog](https://docs.notionapps.com/automation/advanced-reference/step-catalog).
{% endhint %}

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

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](https://docs.notionapps.com/automation/webhooks-and-integrations/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

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

## 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

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

They are designed to stack. Shared token, separate policies.

### 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

```
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)

```
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

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

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](https://docs.notionapps.com/automation/start-from-a-wizard).

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

## Worked examples

### 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

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”

**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

**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

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](https://docs.notionapps.com/screens-and-components/types-of-screens/native-automation-and-operational-screen-guides/linked-app-exchange-screen).

## Inbound request shapes (do not mix)

### Workflow inbound

```http
POST {API}/v1/webhooks/workflow-events
POST {API}/v1/application/{APPLICATION_ID}/workflows/webhook-events
```

```json
{
  "application_id": "APP_ID",
  "target_type": "application",
  "event_id": "evt_001",
  "idempotency_key": "partner:evt_001",
  "payload": {
    "status": "approved",
    "name": "Jordan Lee"
  }
}
```

### Messaging inbound

```http
POST {API}/v1/webhooks/messaging-events
POST {API}/v1/application/{APPLICATION_ID}/messaging/webhook-events
```

```json
{
  "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](https://docs.notionapps.com/automation/webhooks-and-integrations/webhook-technical-reference).

## 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

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.

## Builder checklist

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

{% hint style="success" %}
If someone says “the webhook failed” or “the webhook worked but nothing happened,” ask **which URL, which User-Agent, and what the success JSON was** (`runs_created` vs `message_id` / `deliveries_created`). That identifies the product before you open the app.
{% endhint %}

## Related

* [Webhooks and Integrations](https://docs.notionapps.com/automation/webhooks-and-integrations) — Call webhook and wait setup
* [Webhook Technical Reference](https://docs.notionapps.com/automation/webhooks-and-integrations/webhook-technical-reference) — URLs, tokens, curl
* [Messaging and Notifications](https://docs.notionapps.com/automation/messaging-and-notifications) — channels, topics, subscribers
* [Application Messaging Foundation Guide](https://docs.notionapps.com/automation/application-messaging-foundation-guide) — contracts and External API
* [Workflow Foundation User Guide](https://docs.notionapps.com/automation/workflow-foundation-user-guide) — steps and runs
* [Step catalog](https://docs.notionapps.com/automation/advanced-reference/step-catalog) — `call_webhook_and_wait`, `send_notification`, `publish_message`
* [28 Aug 2026 Release](https://docs.notionapps.com/release-notes/28-aug-2026-release) — live screen updates
* [How does messaging work with workflows?](https://docs.notionapps.com/troubleshooting/automation-how-does-messaging-work-with-workflows)
