> 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/notionapps-messaging-how-to-guide.md).

# 💬 NotionApps Messaging How-To Guide

## NotionApps Messaging How-To Guide

### NotionApps Messaging How-To Guide

![Messaging routing concept](https://4233028229-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5ZqDXcVVffWUqEIZVhmn%2Fuploads%2F3N99vYj45F6n5fDVAAzZ%2Fnotionapps-messaging-screenshot-gallery.png?alt=media\&token=b62bd223-5268-48da-852d-051c24d62ba5)

> Support visual: this image shows Messaging as a routing hub. Events can move from screens, workflows, broadcasts, linked apps, and webhooks through validated routes to the destinations that subscribe to them.

> Current state: Messaging is now documented as one engine inside **Automation (Beta Testing)**. Use Messaging for channels, direct conversations, group conversations, announcements, notifications, linked app events, and workflow-aware replies. Use the NotionApps Automation Current State Guide for the latest packaging, entitlement, and provisioning language.

Messaging Foundation is the routing and communication layer for a NotionApps application. It lets app events, workflows, announcements, linked apps, webhooks, and subscribers communicate through structured messages.

Use this guide when you need to configure channels, topics, contracts, publishers, subscribers, announcements, request/reply flows, linked app handoffs, or webhook routes.

For the complete cross-tool explanation, examples, support playbooks, and entitlement model, see the Automation, Workflow, and Messaging Support Handbook.

#### How To Use This Guide

Start with the outcome-first setup path if you are new to Messaging. A maker should be able to say what they want in business language, answer a few numbered questions, and let NotionApps assemble the route objects. Use the concepts section when you need to understand or troubleshoot the channels, topics, contracts, publishers, and subscribers that the platform created.

#### When To Use Messaging

Use Messaging when the main problem is routing an event.

Good Messaging use cases:

* A button click should notify more than one destination.
* A workflow should publish a reusable event.
* One workflow should ask another workflow for a decision.
* An app owner should broadcast a message to all app users.
* One app should send an event to another app.
* A record change should call a webhook.
* An external system should send a structured event into NotionApps.
* You need route activity, validation, and subscriber visibility.

Use Workflow when the main problem is doing a sequence of work.

Use Automation when the builder wants a guided recipe that may generate Workflow, Messaging, or both.

#### What Messaging Is Not

Messaging is not the same thing as email delivery.

Messaging can create the event that should result in an email. A Workflow Notify step, webhook, subscriber, or future delivery provider sends the actual email.

For native outbound email today, use Workflow Foundation's Notify step with:

```json
{
  "channel": "email",
  "recipients": ["person@example.com"],
  "subject": "Message subject",
  "body": "Message body"
}
```

Messaging is also not a replacement for Workflow. Workflow makes decisions, updates records, waits, branches, and performs multi-step work. Messaging moves structured events to the right places.

#### Where To Find Messaging

Open an application in the builder and select **Automation > Messaging** or open the advanced **Messaging** builder when available.

Messaging contains these pages:

| Page          | Use it for                                                                                                                                                                                                                    |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Overview      | The progressive messaging home. New work starts with the messaging outcome; existing work shows route setup, testing, failures, or management.                                                                                |
| Guided setup  | The deeper setup workspace for describing the business result, answering source/destination questions, reviewing route details, creating the channel, contract, publisher, subscriber, and running a dry-run from one action. |
| Announcements | App-wide in-app broadcast messages.                                                                                                                                                                                           |
| Channels      | Message lanes and allowed topics.                                                                                                                                                                                             |
| Contracts     | Payload schemas and examples.                                                                                                                                                                                                 |
| Publishers    | Sources that can publish and destinations that can subscribe.                                                                                                                                                                 |
| Activity      | Dry-run, publish test messages, and inspect delivery.                                                                                                                                                                         |

#### Start With Business Language

The Messaging home experience now starts the same way Workflow setup starts: with the maker describing the business outcome.

Instead of asking a maker to begin with a channel, topic, contract, publisher, and subscriber, the setup page asks:

```
What should happen?
```

The maker can type normal language, such as:

```
When someone submits a stock count and the numbers do not match, notify operations and start a review conversation.
```

NotionApps then recommends a route pattern and fills in the technical objects that Messaging needs.

**Messaging Overview Progressive Path**

Messaging Overview is the safest starting place for a maker who does not already know the channel, topic, contract, publisher, or subscriber.

It shows one current required step:

1. **Start with the outcome**: the maker types what the message should accomplish or chooses a starter such as notify, broadcast, conversation, or external event.
2. **What NotionApps will build**: appears after the outcome exists. It explains the recommended route and asks for the real source and destination.
3. **Create the messaging route**: appears when the source and destination are ready. One action creates or reuses the channel, contract, publisher, subscriber, and optional dry-run path.
4. **Test the route**: appears when route objects exist and live publish access allows a dry-run preview.
5. **No required action right now**: appears when the app already has messaging work to manage.

Secondary tools such as **Continue work**, **Announcements**, **Manual objects**, and **Manage / monitor** appear only after there is messaging work to manage. This keeps a new maker focused on the current setup decision instead of exposing every advanced page up front.

**What The Outcome Panel Does**

The outcome panel helps the maker:

1. Describe the desired result in normal business language.
2. Review a recommended setup pattern.
3. Answer numbered questions about where the message starts, where it should go, and what payload should travel with it.
4. Review the generated channel, topic, contract, source type, destination type, fields, and example payload.
5. Build the route in one action. NotionApps creates or reuses the channel, saves and publishes the contract, attaches the publisher, attaches the subscriber, and dry-runs the route when live publish access allows it.

This keeps Messaging consistent with Workflow's direction: the maker describes the work, and NotionApps assembles the engine.

**Current Outcome Patterns**

| Pattern                          | Use it when                                                                                                        | Generated route shape                         |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------- |
| Notify the right person          | A form, button, screen, or workflow event should alert a supervisor, reviewer, owner, or operations queue.         | `notifications / work.notification.requested` |
| Broadcast an announcement        | An app admin needs to publish a maintenance notice, policy update, or service announcement to everyone.            | `announcements / announcement.published`      |
| Start or continue a conversation | People need a direct or contextual message thread tied to a request, approval, item, incident, or customer record. | `conversations / conversation.message.sent`   |
| Send an event to another system  | A workflow, form, or app event should notify a webhook, linked app, or external system.                            | `integrations / external.event.created`       |

The patterns are starting points. After applying one, the maker can still edit the route details before creating objects.

**The Messaging Setup Flow**

Messaging Overview uses the same steps progressively. The Guided setup page exposes the full setup workspace when the maker or support team needs to inspect or adjust the route details directly:

1. **Describe what should happen**

   Type the business outcome. NotionApps recommends a route pattern.
2. **Answer the numbered questions**

   Choose the source type, source object, destination type, destination object, and payload. The source and destination fields are context-aware, so makers can search screens, forms, controls, data sources, workflows, and saved IDs instead of remembering raw identifiers.
3. **Review the route plan**

   NotionApps explains the route in plain language and highlights anything missing before it can be created safely.
4. **Build this messaging route**

   One action creates the required Messaging objects in the correct order and runs a dry-route preview when the app has live publish access. Advanced makers can still inspect or refine the generated channel, contract, publisher, subscriber, and activity afterward.

**Example: Notify A Supervisor After A Form Submission**

Business outcome:

```
When an employee submits a timecard, notify the assigned supervisor.
```

Recommended pattern:

```
When this happens, notify the right person.
```

NotionApps fills in:

| Object           | Example value                           |
| ---------------- | --------------------------------------- |
| Channel          | `notifications`                         |
| Topic            | `work.notification.requested`           |
| Contract         | `notification_request@1.0.0`            |
| Source type      | `form`                                  |
| Destination type | `workflow`                              |
| Example payload  | record id, message, recipient, priority |

Maker actions:

1. Choose the actual form in the Source field.
2. Choose the actual workflow or notification destination in the Destination field.
3. Review the route plan.
4. Select **Build this messaging route**.
5. Confirm the dry-run shows the expected destination.

**Example: Create An App-Wide Broadcast**

Business outcome:

```
Broadcast a maintenance notice to every person using this application.
```

Recommended pattern:

```
Broadcast an announcement to everyone.
```

NotionApps fills in:

| Object           | Example value                  |
| ---------------- | ------------------------------ |
| Channel          | `announcements`                |
| Topic            | `announcement.published`       |
| Contract         | `announcement_broadcast@1.0.0` |
| Source type      | `workflow`                     |
| Destination type | `screen`                       |
| Example payload  | title, message, severity       |

For simple app-wide announcements, makers can also use the dedicated **Announcements** page. Use the guided setup route when the broadcast should be generated by a workflow, screen action, or integration event.

#### Start In Automation When You Want Guardrails

Use **Automation** before opening the advanced Messaging builder when the maker knows the business outcome but does not yet know the channel, topic, contract, publisher, or subscriber.

Automation adds three maker-friendly steps:

1. **Preflight checks**: confirms Messaging entitlement, route support, app context, saved recipes, generated objects, and Activity visibility.
2. **Plain-language setup**: asks when the event happens, who receives it, what result should occur, and what data should travel with the message.
3. **Guided testing**: previews the route, exposes blockers, generates objects, then asks the maker to test as the real user and verify Activity.

Use Messaging directly when you already know the route primitives.

Use Automation when the maker says:

```
Broadcast this to everyone.
Notify a manager after this form is submitted.
Send this event to a workflow and a webhook.
Let the supervisor and employee discuss this timecard.
```

After Automation generates the route, open **Automation > Messaging** to inspect or refine the channel, contract, publisher, subscriber, announcement, or delivery activity. If the maker is starting directly inside Messaging, use **Overview** first. Open **Guided setup** when they need the full setup workspace, and use the manual Channels, Contracts, Publishers, and Activity pages only when an advanced builder needs to tune the generated objects.

#### Core Concepts

**Channel**

A channel is a message lane.

Examples:

```
notifications
approvals
tasks
orders
announcements
integrations
```

Use lowercase names that will still make sense months later.

**Topic**

A topic describes the specific event inside a channel.

Examples:

```
task.assigned
task.completed
approval.requested
approval.decided
order.created
admin.broadcast
```

Prefer `noun.action` or `domain.event`.

**Contract**

A contract describes the payload a message should carry.

Example contract:

```json
{
  "name": "task_assignment",
  "version": "1.0.0",
  "fields": [
    {
      "key": "task_id",
      "label": "Task ID",
      "type": "record_reference",
      "required": true
    },
    {
      "key": "assignee_email",
      "label": "Assignee email",
      "type": "email",
      "required": true
    },
    {
      "key": "due_date",
      "label": "Due date",
      "type": "datetime",
      "required": false
    }
  ]
}
```

Contracts help builders catch missing fields before live delivery.

**Publisher**

A publisher is a source allowed to publish messages.

Publisher examples:

* Button.
* Screen/form.
* Workflow.
* External API.
* Admin operation.
* Linked app.

**Subscriber**

A subscriber is a destination listening for messages.

Subscriber examples:

* Workflow.
* App screen.
* Announcement.
* Webhook.
* Linked app.
* Admin activity route.

**Message**

A message is one event sent through Messaging.

It usually has:

* Channel.
* Topic.
* Message type.
* Contract name and version.
* Payload.
* Source.
* Subscriber delivery records.

#### Entitlement Levels

Messaging behavior is gated. The exact source may be subscription, account, app, or admin override.

Common levels:

| Level                  | Enables                                               |
| ---------------------- | ----------------------------------------------------- |
| `builder_draft_only`   | Configure and preview drafts without live delivery.   |
| `intra_app_enabled`    | Same-app messaging, workflows, and announcements.     |
| `inter_app_enabled`    | Linked app routing.                                   |
| `external_api_enabled` | Webhooks and external API routes.                     |
| `admin_enabled`        | Admin-level operations and elevated support controls. |

Use the lowest level that supports the route. Do not request external API access for a simple in-app announcement.

#### How To Build: App-Wide Service Announcement

Goal:

```
Show a maintenance notice on every live app screen.
```

**Step 1: Open Announcements**

Open **Messaging > Announcements**.

**Step 2: Create Announcement**

Set:

| Field      | Value                                                       |
| ---------- | ----------------------------------------------------------- |
| Title      | `Scheduled maintenance`                                     |
| Message    | `The app will be unavailable tonight from 9-10 PM Eastern.` |
| Severity   | `warning`                                                   |
| CTA label  | `View details`                                              |
| CTA URL    | Optional status page                                        |
| Starts at  | Optional start time                                         |
| Expires at | Optional end time                                           |

**Step 3: Publish Or Activate**

Save the announcement. If the builder exposes an active state, activate it.

**Step 4: Test**

Open several live app screens and verify the announcement appears.

Use this for:

* Maintenance windows.
* Data freeze notices.
* "Timesheets due today" reminders.
* Critical policy changes.
* Temporary outage warnings.

#### How To Build: Button Publishes Task Assignment

Goal:

```
When a manager clicks Assign task, publish task.assigned and start a workflow.
```

**Step 1: Create Channel**

Open **Channels** and create:

```
tasks
```

Allowed topic:

```
task.assigned
```

**Step 2: Create Contract**

Open **Contracts** and create:

```
task_assignment@1.0.0
```

Fields:

| Key              | Type               | Required | Example                |
| ---------------- | ------------------ | -------- | ---------------------- |
| `task_id`        | `record_reference` | Yes      | `rec_123`              |
| `task_title`     | `string`           | Yes      | `Review client intake` |
| `assignee_email` | `email`            | Yes      | `manager@example.com`  |
| `due_date`       | `datetime`         | No       | `2026-06-30T17:00:00Z` |

**Step 3: Create Publisher**

Open **Publishers**.

Create a publisher:

| Field       | Value                    |
| ----------- | ------------------------ |
| Source type | `Button`                 |
| Source      | Search for `Assign task` |
| Channel     | `tasks`                  |
| Topic       | `task.assigned`          |
| Contract    | `task_assignment@1.0.0`  |

**Step 4: Create Workflow Subscriber**

Create a subscriber:

| Field           | Value                      |
| --------------- | -------------------------- |
| Subscriber type | `Workflow`                 |
| Destination     | `Notify assignee workflow` |
| Channel         | `tasks`                    |
| Topic           | `task.assigned`            |
| Contract        | `task_assignment@1.0.0`    |

**Step 5: Configure The Workflow**

In Workflow, create a workflow triggered by **Message received**.

Set:

| Field            | Value             |
| ---------------- | ----------------- |
| Message channel  | `tasks`           |
| Message topic    | `task.assigned`   |
| Contract name    | `task_assignment` |
| Contract version | `1.0.0`           |

Notify step:

| Field     | Value                                       |
| --------- | ------------------------------------------- |
| Channel   | `Email`                                     |
| Recipient | `payload.assignee_email`                    |
| Subject   | `New task assigned`                         |
| Body      | `A task was assigned to you in NotionApps.` |

**Step 6: Dry-Run**

Open **Activity** and publish a test message:

```json
{
  "task_id": "rec_123",
  "task_title": "Review client intake",
  "assignee_email": "manager@example.com",
  "due_date": "2026-06-30T17:00:00Z"
}
```

Confirm Messaging finds the workflow subscriber.

#### How To Build: Workflow Publishes A Message

Goal:

```
After a workflow creates a record, publish order.ready_for_fulfillment.
```

**Messaging Setup**

Channel:

```
orders
```

Topic:

```
order.ready_for_fulfillment
```

Contract:

```
order_ready@1.0.0
```

Fields:

| Key              | Type               | Required |
| ---------------- | ------------------ | -------- |
| `order_id`       | `record_reference` | Yes      |
| `customer_email` | `email`            | Yes      |
| `total`          | `number`           | No       |

**Workflow Setup**

In Workflow, add a **Publish message** step after the record is created.

Set:

| Field            | Value                                      |
| ---------------- | ------------------------------------------ |
| Channel          | `orders`                                   |
| Topic            | `order.ready_for_fulfillment`              |
| Contract name    | `order_ready`                              |
| Contract version | `1.0.0`                                    |
| Payload          | Map from `payload` and earlier step output |

Example payload:

```json
{
  "order_id": "context.step_create_order.record_id",
  "customer_email": "payload.customer_email",
  "total": "payload.total"
}
```

**Subscriber Options**

The message can route to:

* Fulfillment workflow.
* External webhook.
* Linked operations app.
* Activity-only subscriber for audit.

#### How To Build: Request And Reply Between Workflows

Goal:

```
Workflow A asks Workflow B for approval and waits for a reply.
```

**Messaging Concepts**

Use a request topic and a reply topic.

```
approvals/approval.requested
approvals/approval.decided
```

**Workflow A**

Add **Send message request**.

Set:

| Field           | Value                |
| --------------- | -------------------- |
| Channel         | `approvals`          |
| Topic           | `approval.requested` |
| Reply channel   | `approvals`          |
| Reply topic     | `approval.decided`   |
| Timeout seconds | `86400`              |

Workflow A should then wait for the matching reply or follow the timeout path.

**Workflow B**

Trigger:

```
Message received
```

Channel/topic:

```
approvals / approval.requested
```

Workflow B notifies a manager, records a decision, then adds **Send message reply**.

Reply payload:

```json
{
  "decision": "approved",
  "decided_by": "manager@example.com",
  "notes": "Approved for launch"
}
```

**Why This Pattern Helps**

Use request/reply when:

* One process needs another team to decide.
* The first workflow should pause instead of polling.
* You need audit records for both request and reply.
* You need a timeout path.

#### How To Build: Linked App Handoff

Goal:

```
A customer portal app sends an approved onboarding request to an operations app.
```

Required entitlement:

```
inter_app_enabled
```

**Source App**

Create channel/topic:

```
onboarding/onboarding.approved
```

Create contract:

```
onboarding_approval@1.0.0
```

Fields:

| Key              | Type     |
| ---------------- | -------- |
| `customer_name`  | `string` |
| `customer_email` | `email`  |
| `plan`           | `string` |
| `approved_by`    | `email`  |

Create publisher from Workflow or Button.

**Destination App**

Create subscriber:

| Field           | Value                      |
| --------------- | -------------------------- |
| Subscriber type | `Linked app` or `Workflow` |
| Destination     | Operations workflow        |
| Channel/topic   | Match the source route     |

**Test**

Use Activity in both apps.

Confirm:

* Source message is accepted.
* Contract validates.
* Linked app delivery occurs.
* Destination workflow receives the payload.

#### How To Build: Webhook Subscriber

Goal:

```
When a record is approved, notify an external service.
```

Required entitlement:

```
external_api_enabled
```

**Messaging Setup**

Channel/topic:

```
integrations/record.approved
```

Subscriber:

| Field              | Value                                    |
| ------------------ | ---------------------------------------- |
| Subscriber type    | `Webhook`                                |
| Destination        | `https://example.com/notionapps-webhook` |
| Payload visibility | `metadata_only`, `redacted`, or `full`   |

Use `full` only when the receiving service is trusted and needs the whole payload.

**Test Payload**

```json
{
  "record_id": "rec_456",
  "status": "approved",
  "approved_by": "manager@example.com"
}
```

#### Activity And Dry-Run

Use **Activity** to:

* Publish a test message.
* Preview subscriber matches.
* Verify contract validation.
* Inspect recent messages.
* See delivery status.
* Identify missing subscribers.
* Confirm webhook/linked app routing.

Dry-run before live delivery when:

* A route has multiple subscribers.
* A contract is new.
* Payload visibility matters.
* Webhooks are involved.
* A workflow should start from a message.

When the route was created from Automation, follow the Guided testing panel first:

```
Run preview
-> fix blockers
-> generate route objects
-> trigger as the real app user
-> confirm the message in Activity
```

Then use Messaging Activity for lower-level delivery details.

#### Messaging With Native Automation Screens

Messaging can send events to workflows, webhooks, linked apps, and native app screens. Native screens make message state visible to app users and owners.

Common pairings:

| Messaging need                                    | Native screen       |
| ------------------------------------------------- | ------------------- |
| Broadcast service announcement                    | Notification Center |
| Show an ongoing support or approval discussion    | Conversation        |
| Route a message to work that a person must claim  | Work Queue          |
| Ask a user to approve, reject, or request changes | Decision            |
| Show linked app exchanges                         | Linked App Exchange |
| Monitor message-driven operations                 | Operator Console    |

For detailed setup and troubleshooting for each screen type, see the Native Automation And Operational Screen Guides.

When configuring a native screen:

1. Add the screen in the application builder.
2. Choose the screen type by its distinct icon and title.
3. Open the screen configuration.
4. Set **Listen scope**.
5. For conversation/message screens, search channel, topic, conversation, or correlation suggestions instead of typing IDs.
6. For linked app screens, search linked applications instead of copying application IDs.
7. If the action should send the user somewhere after they acknowledge or reply, choose the **After-action screen** from the context-aware picker.

Hidden screens can appear in the picker. This is expected when the screen is hidden from navigation but still reachable through a workflow or messaging route.

**Private Conversations Versus Announcements**

Use Conversation when the message belongs to one record, workflow run, review, ticket, or support exchange. Use announcements or Notification Center when the message is for everyone.

Private conversation pattern:

```
employee submits timecard
-> workflow publishes message with conversation_id = timecard id
-> supervisor replies with the same conversation_id
-> employee replies with the same conversation_id
```

Announcement pattern:

```
admin publishes payroll deadline reminder
-> every allowed app user sees the announcement
-> no private conversation is created
```

For private conversations, include participant fields in the payload whenever possible:

```json
{
  "timecard_id": "tc_123",
  "employee_email": "employee@example.com",
  "supervisor_email": "supervisor@example.com",
  "message": "Please clarify overtime for Tuesday."
}
```

This lets NotionApps show the conversation to the right people without exposing raw route IDs to the maker or the end user.

#### Troubleshooting

**No Matching Subscriber**

Check:

* Channel spelling.
* Topic spelling.
* Subscriber active state.
* Contract name/version.
* Entitlement level.
* Source and target app IDs for linked app routes.

**Contract Validation Fails**

Check:

* Required fields exist.
* Email fields contain valid email strings.
* Number fields are numbers.
* Date fields are ISO-like date/time values.
* Field names match exactly.

**Workflow Does Not Start From Message**

Check:

* Subscriber type is `Workflow`.
* Subscriber destination is the correct workflow.
* Workflow trigger is `Message received`.
* Workflow channel/topic/contract match the message.
* Workflow is published.

**Announcement Does Not Appear**

Check:

* Announcement is active.
* Starts at and expires at are correct.
* Severity is allowed.
* User is viewing a live app screen.
* Messaging entitlement supports announcements.

**Webhook Does Not Receive**

Check:

* Entitlement allows external API/webhook delivery.
* Endpoint URL is valid.
* Subscriber is active.
* Payload visibility is not blocking needed fields.
* Activity shows delivery attempt or failure.

#### Best Practices

* Use channel names for domains, not one-off actions.
* Use topic names for specific events.
* Use contracts for every reusable route.
* Include realistic payload examples.
* Keep contract versions stable once published.
* Use new contract versions for breaking payload changes.
* Prefer Workflow when you need decisions or multi-step work.
* Prefer Messaging when you need routing, subscribers, linked apps, or webhooks.
* Prefer Automation recipes when a non-technical builder owns the setup.
* Use Activity dry-run before publishing a new route.
* Use native Notification Center or Conversation screens when messages need to remain visible inside the app.
* Configure native screen source binding from context-aware pickers, not memorized IDs.
* Confirm hidden screens are intentional route destinations before removing them.

#### Messaging Checklist

Before going live:

* Automation Preflight has no Messaging blockers when the route was created from a recipe.
* Automation Guided testing preview has run when a non-technical maker owns setup.
* Entitlement supports the route.
* Channel exists.
* Topic is allowed or intentionally open.
* Contract exists with realistic examples.
* Publisher source is correct.
* Subscriber destination is correct.
* Payload visibility is intentional.
* Dry-run finds the expected subscriber.
* Workflow subscribers are published.
* Webhook subscribers have valid URLs.
* Announcement schedules are correct.
* Native message screens have the right listen scope, channel, topic, and action destination.
* Activity shows expected route behavior.
