Skip to content

Messaging

The messaging service provides an internal communication channel between an app and its blocks. This allows for coordination and information sharing within an app installation, without the overhead or visibility of the events system.

Overviewยป

Internal messages are a lightweight mechanism for communication between components within the same app installation:

  • App-to-block communication: The app can send messages to specific blocks it manages
  • Block-to-app communication: Blocks can send messages to their parent app
  • Block-to-block communication: Blocks can send messages to other blocks within the same app
  • Private communication: Unlike events, messages are not visible in the UI and don't establish connections
  • Coordinated delivery: Messages are collected during execution and delivered as a batch

This service is particularly useful for:

  • Routing incoming webhook payloads to interested blocks
  • Coordinating related blocks without visible event connections
  • Broadcasting information to multiple blocks
  • Implementing pub/sub patterns within an app

Scope and boundariesยป

Internal messaging is strictly bounded by the app installation:

  • Messages can only flow between components within the same app installation
  • Blocks can message other blocks, but must know their block IDs
  • Messages cannot cross app installation boundaries under any circumstances
  • All messaging routes are defined in code by developers, not by user-configured connections

This boundary provides a clean separation of concerns and ensures that apps have complete control over their internal communication.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ App Installation โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                                      โ”‚
โ”‚       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                 โ”‚
โ”‚       โ”‚        โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚        โ”‚                 โ”‚
โ”‚       โ”‚ Block  โ”‚          โ”‚  App   โ”‚                 โ”‚
โ”‚       โ”‚   A    โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚        โ”‚                 โ”‚
โ”‚       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚        โ”‚                 โ”‚
โ”‚            โ–ฒ              โ”‚        โ”‚                 โ”‚
โ”‚            โ”‚              โ”‚        โ”‚                 โ”‚
โ”‚            โ–ผ              โ”‚        โ”‚                 โ”‚
โ”‚       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚        โ”‚                 โ”‚
โ”‚       โ”‚        โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚        โ”‚                 โ”‚
โ”‚       โ”‚ Block  โ”‚          โ”‚        โ”‚                 โ”‚
โ”‚       โ”‚   B    โ”‚          โ”‚        โ”‚                 โ”‚
โ”‚       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                 โ”‚
โ”‚                                                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
    โ”‚                                             โ”‚
    ร—             Cannot cross boundary           ร—
    โ”‚                                             โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Another App โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                                    โ”‚
โ”‚                                                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Messaging vs. eventsยป

For user-visible workflow orchestration, use the events system instead. Messages are for internal app coordination only.

Feature Internal messages Events
Visibility Private to the app Visible in UI
Connections Implicit Explicit socket connections
Routing Code-controlled Connection-based
Scope Within a single app installation only Can span across different apps
Configuration Defined by developers in code Defined by users in the UI
Use case Internal coordination Workflow orchestration
UI presence No UI representation Visible in event history
Recipient awareness Dynamic, determined at runtime Static, based on connections

Communication patternsยป

The internal messaging system supports these patterns:

Pattern Supported Notes
App โ†’ Specific block โœ“ Yes Direct communication
App โ†’ Multiple blocks โœ“ Yes Broadcast or targeted group
Block โ†’ Parent app โœ“ Yes Direct communication
Block โ†’ Block (same app) โœ“ Yes Must know the receiving block ID
Block โ†’ Block (different app) โœ— No Impossible with messaging (use events instead)

Sending messagesยป

Sending to the appยป

Any block can send messages to its parent app using:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import { messaging } from "@slflows/sdk/v1";

// Send a message to the parent app
await messaging.sendToApp({
  body: {
    type: "status_update",
    blockId: input.block.id,
    status: "ready",
    timestamp: Date.now(),
  },
});

Sending to blocksยป

Both apps and blocks can send messages to specific blocks using:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import { messaging } from "@slflows/sdk/v1";

// Send a message to specific blocks
await messaging.sendToBlocks({
  blockIds: ["block-123", "block-456"],
  body: {
    type: "config_update",
    newSettings: { timeout: 30 },
  },
});

Discovering blocksยป

Use the blocks.list() function to discover blocks within your app installation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { messaging, blocks } from "@slflows/sdk/v1";

// Discover blocks of a specific type
const { blocks: targetBlocks } = await blocks.list({
  typeIds: ["dataProcessor"],
});

// Send message to discovered blocks
await messaging.sendToBlocks({
  blockIds: targetBlocks.map(b => b.id),
  body: {
    type: "data_update",
    payload: newData,
  },
});

Service discovery

Use blocks.list() to discover other blocks within your app installation. This is particularly useful when you need to coordinate with blocks of a specific type without hard-coding block IDs.

Receiving messagesยป

To receive messages in an app or a block, implement the onInternalMessage handler:

1
2
3
4
5
6
{
  onInternalMessage: async (input) => {
    const { message } = input;
    console.log(`Received message ${message.body}`);
  },
}

Message body

Flows does not enforce any specific structure on the message body. It can be any JSON-serializable object. It's up to the sender and receiver to agree on the structure and content of the messages, but given that they're internal to one app, this agreement is usually straightforward.

Transaction modelยป

Internal messages are not sent immediately when the sendToApp or sendToBlocks functions are called. Instead:

  1. Messages are collected during the current execution
  2. When the execution completes, all messages are delivered in a batch
  3. This ensures that messaging operations are part of the overall transaction

This model has important implications:

  • Messages won't be delivered if the execution fails
  • You won't receive a response to your message in the same execution
  • Multiple messages can be sent in a single execution and will be delivered together