Skip to main content

Message Push

Message push flows turn an external HTTP request into a Soln notification. The external system posts a JSON payload to the flow webhook, the flow renders that payload into a message, and the message is delivered to an app inbox or a group chat.

A message push flow belongs to an OpenLink system and differs from inbound and outbound flows:

  • It has no form binding and never creates, updates, or deletes local form records
  • Its canvas contains exactly one start node, at least one message node, and one end node. Source, transform, target, branch, and query nodes are not allowed
  • It is always triggered by webhook; poll, push, and service call triggers are not available
  • An agent execution node may run before the message node. When it does, the agent output becomes the message body

Message Node Configuration​

Select the message node in the design canvas to configure how the notification is delivered.

FieldDescription
Delivery typeInbox message sends an app message; Group chat message posts into a group chat
App channelInbox only. A non-Luna app channel that receives the message
Message templateInbox only. An active inbox template. Because a message push flow has no form binding, the template cannot be a form record template
RecipientsInbox only. Fixed members, a form member field, or members resolved from the request body
Sender agentGroup chat only. Provides the sending identity inside the group chat. The message node does not execute the agent
Target group chatGroup chat only. The group chat must already contain the sender agent
Execution conditionAlways, On success, or On failure. It applies when no agent execution node feeds the message

Message Body​

SituationMessage body
An agent execution node ran before the messageThe agent output text, sent as-is
No agent node, and the execution failedThe failure reason
No agent node, and the payload has a top-level markdown stringThat Markdown, sent as-is
No agent node and no failureMarkdown rendered from the webhook payload or the upstream result

Markdown rendering turns objects into - **field**: value lines, renders arrays of objects inline (- **code**: A · **qty**: 2), skips empty values, and truncates oversized bodies. The sender agent in a group chat is only a carrier: without an agent execution node, the flow does not call AI to produce the content.

How To Request​

ItemValue
MethodPOST
URL<your-runtime-origin>/api/open-link/message-push/<flowCode>
Content typeapplication/json; other content types return OPEN_LINK_CONTENT_TYPE_INVALID
HeadersX-Idempotency-Key is optional; add the inbound authentication headers configured for that system, see Inbound Auth
BodyOne JSON object, with the parameters listed below; a body that is not a JSON object (an array or a single value) fails with OPEN_LINK_EXECUTION_PAYLOAD_INVALID

<your-runtime-origin> is the address of the workspace runtime, the same address you use to open Soln in the browser, such as https://runtime.example.com. <flowCode> is the flow code shown on the flow record: a generated string such as open_link_flow_889b5ea7abae, not the flow definition UUID. The flow's start node in the design canvas shows the complete URL for that flow, so copy it from there instead of building it by hand:

https://runtime.example.com/api/open-link/message-push/open_link_flow_889b5ea7abae

Message push has its own endpoint: the inbound webhook URL is reserved for inbound flows and rejects message push flows.

Request Parameters​

Every request body parameter is optional. Pick the ones the message node needs; one request can carry several of them.

ParameterTypeRequiredDescription
markdownstringNoCustom group chat body, sent as Markdown without changes
memberIdsstring[]NoRecipients provided by the request body: member UUIDs or external member identifiers, for example ["1001"], see below
messageobjectNoInbox message template variables, keyed by the template variable name
Any other fieldAnyNoInput for an agent execution node, or for node input mappings that read input.* and record.data.*

Request Examples​

Group chat message with a Markdown body you wrote (the message node must use Group chat message):

curl --request POST \
'https://runtime.example.com/api/open-link/message-push/notification-push' \
--header 'Content-Type: application/json' \
--header 'X-OpenLink-Token: ${OPENLINK_TOKEN}' \
--header 'X-Idempotency-Key: notification-001' \
--data '{
"markdown": "> Life is a long stretch of dull hours, with the rose of joy only rarely scattered among them;\n> and whoever would be the first to take that rose of joy is the first to be stung by its thorns.\n\n— Thomas Moore"
}'

Group chat message whose body is rendered from the payload, with recipients passed in memberIds:

curl --request POST \
'https://runtime.example.com/api/open-link/message-push/order-alert-push' \
--header 'Content-Type: application/json' \
--header 'X-OpenLink-Token: ${OPENLINK_TOKEN}' \
--data '{
"memberIds": ["1001"],
"stage": "review",
"owner": "A. Lee",
"tags": ["draft", "review"]
}'

Inbox message with template variables in message (the example template declares quote and signature):

curl --request POST \
'https://runtime.example.com/api/open-link/message-push/quote-push' \
--header 'Content-Type: application/json' \
--header 'X-OpenLink-Token: ${OPENLINK_TOKEN}' \
--data '{
"message": {
"quote": "Freedom consists less in doing what one wants than in not doing what one does not want.",
"signature": "------ Jean-Jacques Rousseau <Reveries of a Solitary Walker>"
}
}'

Send X-OpenLink-Token only when the OpenLink uses shared-token inbound authentication; use the configured header name. For signature authentication, send the configured app ID, timestamp, and HMAC signature headers instead. The signature must cover this request's exact raw JSON body.

Request Notes​

Group chat body. The body is chosen in this order: agent execution output, then the execution failure reason, then a markdown field in the payload, then Markdown generated from the payload. Without a markdown field the payload is rendered: every field becomes one - **field**: value line, a nested object becomes an indented sub-list, an array of objects is inlined as - **date**: 2026-09-15 · **hours**: 2, empty or null values are skipped, and a body longer than 3800 characters is truncated. For example:

{ "stage": "review", "owner": "A. Lee", "tags": ["draft", "review"] }
- **stage**: review
- **owner**: A. Lee
- **tags**
- draft
- review

The body must stay within the chat message limit of 4000 characters; a longer body fails with WORKFLOW_GROUP_CHAT_BODY_TOO_LONG. When an agent execution node runs before the message node, its output is always the group chat body, even when the request carries a markdown field; the request then carries the fields the agent analyzes.

Inbox message template. Template variables are read on the message node through the request body mapping type, resolved as input.message.<variable key>, and the mapping key must be exactly the template variable key. The example template body above is written as {{input.quote}} and {{input.signature}}, and the delivered message is ordinary text, not a field list:

Freedom consists less in doing what one wants than in not doing what one does not want.
------ Jean-Jacques Rousseau <Reveries of a Solitary Walker>

If the request is missing a mapped field, the execution fails and the execution log records the missing path, for example RUNTIME_CONTEXT_PATH_NOT_FOUND:input.message.quote.

Recipients from the request body. On the message node open Configure recipients, switch to the From request body scope and tick Member group. The members resolved from the request body then work exactly like fixed recipients: whoever the request resolves to receives the message. Every value in memberIds is first resolved through the subject mappings configured for this OpenLink system; for example, when 1001 is mapped to 00000000-0000-4000-8000-000000000010, that member receives the message. Values that have no mapping and are not member UUIDs are skipped. Recipients are never inferred from other fields, and when nothing resolves the execution fails with MESSAGE_APP_CHANNEL_TARGETS_UNRESOLVED in the log.

Before the request is accepted: the OpenLink system must be active, the flow must be published, the caller must pass the inbound authentication and whitelist rules configured for that system, and requests count against the inbound rate and concurrency protection.

Response​

The endpoint returns 202 Accepted once the execution is queued. The message is delivered asynchronously.

{
"accepted": true,
"duplicate": false,
"logId": "00000000-0000-4000-8000-000000000001",
"traceId": "00000000-0000-4000-8000-000000000002",
"status": "queued",
"definitionId": "00000000-0000-4000-8000-000000000003",
"flowCode": "order-alert-push",
"workflowId": "open-link:...",
"workflowStartDeferred": false,
"callbackRequested": false,
"statusQueryUrl": null
}
FieldDescription
acceptedThe request was accepted for execution
duplicatetrue when the same idempotency key was already accepted; the existing logId and traceId are returned
logId / traceIdExecution identifiers used to find the run in the execution logs
statusInitial execution status, queued
definitionId / flowCodeFlow definition that accepted the request
callbackRequested / statusQueryUrlAlways false and null for message push: this endpoint creates no asynchronous delivery callback

Open the flow execution logs with the returned logId to confirm the message was delivered and to read the failure reason when a run failed. Errors raised while the flow runs, such as an unresolved recipient, are recorded in the execution log rather than returned by the HTTP response.

Common Errors​

CodeMeaning
OPEN_LINK_FLOW_DEFINITION_NOT_FOUNDNo flow matches the flow code
OPEN_LINK_FLOW_DEFINITION_NOT_PUBLISHEDThe flow exists but has not been published
OPEN_LINK_SYSTEM_NOT_ACTIVEThe OpenLink system is not active
OPEN_LINK_FLOW_EXECUTION_TYPE_INVALIDThe flow is not a message push flow; use the inbound webhook or query endpoint instead
OPEN_LINK_CONTENT_TYPE_INVALIDThe request content type is not application/json
OPEN_LINK_JSON_VALUE_INVALIDThe request body is not valid JSON
OPEN_LINK_PAYLOAD_TOO_LARGEThe request body exceeds the size limit
OPEN_LINK_WEBHOOK_AUTH_INVALID / OPEN_LINK_WEBHOOK_TIMESTAMP_EXPIREDInbound authentication rejected the request
OPEN_LINK_WEBHOOK_SOURCE_DENIEDWhitelist rules rejected the caller
OPEN_LINK_INBOUND_RATE_LIMIT_EXCEEDED / OPEN_LINK_INBOUND_CONCURRENCY_LIMIT_EXCEEDEDInbound rate or concurrency protection rejected the request
OPEN_LINK_IDEMPOTENCY_KEY_CONFLICTThe idempotency key was already used by another flow
MESSAGE_TEMPLATE_REQUIRED_INPUT_MAPPING_MISSING:<variable>A required template variable has no input mapping on the message node (raised when publishing)
MESSAGE_TEMPLATE_INPUT_MAPPING_UNKNOWN:<key>The mapping key does not match any template variable key (raised when publishing)
MESSAGE_TEMPLATE_INPUT_MAPPING_PATH_INVALID:<variable>The mapping path is not an allowed runtime path (raised when publishing)
RUNTIME_CONTEXT_PATH_NOT_FOUND:input.message.<variable>The request body does not contain the message field the mapping reads (recorded in the log)
MESSAGE_APP_CHANNEL_TARGETS_UNRESOLVEDThe message node has no fixed recipients and no members resolved from the request body (recorded in the log)

Message push flows are created and published like other flows: in the flow list, or with the OpenLink Runtime commands listed in the OpenLink system commands reference.

  1. Create or select the OpenLink system that owns the notification
  2. Configure inbound authentication and whitelist rules for that system
  3. Create the message push flow with the Runtime command or in the flow list
  4. Open the design canvas, then add and configure the message node and, when the message needs analysis, an agent execution node before it
  5. Save, publish, and enable the flow
  6. Call POST /api/open-link/message-push/<flowCode> from the external system and review the result in the execution logs