Inbound Webhook
External systems write data into Soln through the inbound webhook endpoint: the request body is the flow input, and the flow writes it into the local form according to the canvas mappings.
Only inbound + webhook flows expose this address. Query data and message push flows have their own endpoints, see Query Data and Message Push.
How To Request
| Item | Value |
|---|---|
| Method | POST |
| URL | <runtime-origin>/api/open-link/inbound/webhooks/<flowCode> |
| Content type | Must start with application/json; otherwise the request fails with OPEN_LINK_CONTENT_TYPE_INVALID |
| Body size | At most 10 MiB; a larger body fails with OPEN_LINK_PAYLOAD_TOO_LARGE |
| Authentication | Send the headers of the inbound authentication profile of that OpenLink (shared token, Bearer token, or signature), and pass the whitelist check first |
| Optional headers | X-Idempotency-Key prevents duplicate writes; X-Request-Id is recorded as the external request ID |
<runtime-origin> is the workspace runtime address, the same address you use to open Soln in the browser, and <flowCode> is the flow code such as open_link_flow_0505058add3a. The start node of the flow shows the complete webhook URL, and Request example in the design canvas produces a call example you can copy.
Request Body
The body must be a JSON object. How fields are passed depends on whether the canvas has a transform node.
With A Transform Node: Send External Field Codes
Keys are external field codes and values are the field values:
{
"order_no": "SO-2026-0001",
"amount": 128.5,
"customer": { "code": "C-1001" },
"ordered_at": "2026-09-18T10:00:00.000Z"
}
- Field codes come from the external form field definitions
- Foreign key and relation fields pass the referenced field, such as
customer.code; nested objects are read by path - Fields that no mapping uses are ignored, and extra fields do not affect execution
Without A Transform Node: Send Local Form Field API Names
When the canvas has a target node but no transform node, Soln writes the request body directly as local form data, so keys are local form field API names:
{
"order_no": "SO-2026-0001",
"amount": 128.5
}
Child Table Data
Child table data goes into the input.childTables array:
{
"order_no": "SO-2026-0001",
"input": {
"childTables": [
{
"externalObjectCode": "order_lines",
"rows": [
{ "line_no": "1", "sku": "A-100", "qty": 2 },
{ "line_no": "2", "sku": "A-200", "qty": 1 }
]
}
]
}
}
Each child table is matched to the child table pairs configured on the canvas by externalObjectCode (externalObjectId also works), not by array position, and rows that match no pair are not written.
Optional Envelope And Callback
- When the body only contains
payload(and optionallycallback), Soln uses the content ofpayloadas input data callbackis an asynchronous callback control object:{ "url": "https://...", "headers": { ... } }, used to call back the external system after the flow finishes- A
callbackIdin the body is used as the idempotency key, equivalent toX-Idempotency-Key - The field contract for workflow callback flows is in Workflow Integration
Response
| Situation | Response |
|---|---|
| Accepted | HTTP 202 with accepted, duplicate, logId, traceId, status, flowCode, callbackRequested, and statusQueryUrl |
| Duplicate submission | Submitting the same idempotency key again returns duplicate: true with the existing logId and does not write again |
| Failure | A non-2xx response whose body is { code, requestId, message?, detail? } |
Acceptance only means the request entered the queue. Read the execution result with a GET request to statusQueryUrl:
curl --request GET \
'https://runtime.example.com/api/open-link/inbound/webhooks/executions/<logId>?traceId=<traceId>'
The result contains status, completed, localRecordId, durationMs, errorCode, errorMessage, and the callback delivery state.
Common Errors
| errorCode | Meaning |
|---|---|
OPEN_LINK_CONTENT_TYPE_INVALID | The content type is not JSON |
OPEN_LINK_JSON_VALUE_INVALID | The body is not valid JSON |
OPEN_LINK_PAYLOAD_TOO_LARGE | The body is larger than 10 MiB |
OPEN_LINK_EXECUTION_PAYLOAD_INVALID | The body is not a JSON object |
OPEN_LINK_WEBHOOK_SOURCE_DENIED | The source did not pass the whitelist |
OPEN_LINK_WEBHOOK_AUTH_INVALID | Inbound credential verification failed |
OPEN_LINK_WEBHOOK_TIMESTAMP_EXPIRED | The signature timestamp is outside the allowed clock window |
OPEN_LINK_WEBHOOK_CALLBACK_INVALID | The callback control object is invalid |
OPEN_LINK_FLOW_DEFINITION_NOT_PUBLISHED | The flow has no published version to run |
OPEN_LINK_IDEMPOTENCY_KEY_CONFLICT | The same idempotency key was used for another flow |
OPEN_LINK_INBOUND_RATE_LIMIT_EXCEEDED | The per-minute request limit was exceeded |
OPEN_LINK_INBOUND_CONCURRENCY_LIMIT_EXCEEDED | The maximum concurrent request limit was exceeded |
Call Examples
Both examples use signature authentication: the signature covers METHOD\npath\ntimestamp\nraw body, the timestamp must be the current time, and the signature must be recomputed for the real body. The value in the examples only shows the format.
Main Table Only
curl --request POST \
'https://runtime.example.com/api/open-link/inbound/webhooks/open_link_flow_0505058add3a' \
--header 'Content-Type: application/json' \
--header 'X-App-Id: app_Aqg9LRZahMDuMRS9' \
--header 'X-OpenLink-Timestamp: 1789712190157' \
--header 'X-OpenLink-Signature: <recompute for the real body>' \
--data '{"order_no":"SO-2026-0001","amount":128.5,"customer":{"code":"C-1001"}}'
Main Table With Child Tables
curl --request POST \
'https://runtime.example.com/api/open-link/inbound/webhooks/open_link_flow_0505058add3a' \
--header 'Content-Type: application/json' \
--header 'X-App-Id: app_Aqg9LRZahMDuMRS9' \
--header 'X-OpenLink-Timestamp: 1789712190157' \
--header 'X-OpenLink-Signature: <recompute for the real body>' \
--data '{
"order_no": "SO-2026-0001",
"amount": 128.5,
"customer": { "code": "C-1001" },
"input": {
"childTables": [
{
"externalObjectCode": "order_lines",
"rows": [
{ "line_no": "1", "sku": "A-100", "qty": 2 },
{ "line_no": "2", "sku": "A-200", "qty": 1 }
]
}
]
}
}'
The externalObjectCode of a child table must match the external object code selected in the child table pair on the canvas, and the main fields and input.childTables are submitted in the same body. When the canvas has no transform node, use local form field API names for the main fields instead.
Both examples use signature authentication. For shared token or Bearer token authentication, replace the three signature headers with the matching header, as described in Inbound Auth.