HTTP Sink
The HTTP sink allows you to trigger workflow executions via standard HTTP requests. This is the most flexible sink type, supporting any HTTP method and custom paths.
Endpoint Format
Section titled “Endpoint Format”POST/GET/PATCH/DELETE /api/v1/sink/trigger/http/{app_id}{path}app_id: Your application’s unique identifierpath: Custom path you configure (must start with/)
A service on a device you manage answers at its own address instead; see On a device.
Configuration Options
Section titled “Configuration Options”| Field | Type | Description | Required |
|---|---|---|---|
path | string | URL path for the endpoint (e.g., /webhook) | Yes |
method | string | HTTP method (POST, GET, etc.) | Yes |
auth_token | string | Optional Bearer token for authentication | No |
Local Mode
Section titled “Local Mode”When running locally, the desktop app exposes an HTTP server on port 9657:
http://localhost:9657/{app_id}{path}Example Request
Section titled “Example Request”curl -X POST "http://localhost:9657/app_abc123/webhook" \ -H "Content-Type: application/json" \ -d '{"event": "test", "data": {"key": "value"}}'Remote Mode
Section titled “Remote Mode”When deployed to a server, the endpoint is publicly accessible:
https://your-domain.com/api/v1/sink/trigger/http/{app_id}{path}Example Request
Section titled “Example Request”curl -X POST "https://your-domain.com/api/v1/sink/trigger/http/app_abc123/webhook" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-auth-token" \ -d '{"event": "test", "data": {"key": "value"}}'On a device
Section titled “On a device”Use Device deployment to install and operate the standalone runtime. Service access and port forwarding explains listener addresses, browser access, and private connections to deployed services.
An http event, or an Endpoint (api event), that is deployed to a device is served by the service it is deployed to, at that service’s own address:
http(s)://<service address>:<port>{path}The address has no app_id. The device answers with the event and Flow version the service was deployed with, until the service is updated. The hub, or the desktop app, keeps answering at its own address with the live event.
Who can call it
Section titled “Who can call it”- Who can reach it is the service’s exposure: only this device, one address, or all networks.
- Who may call it follows the service’s access settings. By default, callers send
Authorization: Bearer <token>. The token is shown once after deployment and can be replaced on the service’s Endpoint tab. Choose No token under Endpoint & limits to allow anyone who can reach the listener to call it. This choice applies to every endpoint, Page, chat, and action in that service. Use separate services when they need different access settings. - The event’s
auth_tokenis not used on a device.public_endpointis not read: the service’s access settings apply regardless of whether the endpoint is open on the hub. - Without a certificate on the service, requests and any service token travel unencrypted.
- Senders that cannot set an
Authorizationheader can call a service configured with No token. The service sends no CORS headers, so removing the token requirement does not enable web pages on other origins to call it.
curl -X POST "https://device.example:8443/webhook" \ -H "Authorization: Bearer <service access token>" \ -H "Content-Type: application/json" \ -d '{"event": "test"}'Omit the Authorization header when the service uses No token.
The route
Section titled “The route”A device reads the route as the hub does: path, or path_suffix when there is no path, with a leading / added when it is missing; and method, which is POST when none is saved. The path is literal: at most 2,048 printable ASCII characters without spaces, none of ? # \ { } *, and no . or .. segment. The paths /services, /ui, /ui/… and /channels/… belong to the service itself.
Within one service a method and path can be used once. Two endpoints with the same method and path are refused, and so is an endpoint that uses the route of a chat (POST /chat/{event id}), of a page (GET /pages/{event id}/bootstrap, POST /pages/{event id}/invoke) or of a form or quick action (POST /run/{event id}) of the same service. A device refuses such a service when the deploy is checked, before it replaces a running version. Two services on one device have ports of their own and can use the same route.
Request and answer
Section titled “Request and answer”| Topic | On a device |
|---|---|
| Query fields | Become fields of the payload; a repeated key becomes a list, and key[] always makes one |
| JSON body | Read with Content-Type: application/json; the fields of an object join the query fields |
| Form-encoded body | Read with Content-Type: application/x-www-form-urlencoded, like query fields |
| Other bodies | Passed as one text value; together with query fields the request is refused |
| Multipart bodies (file uploads) | Refused with 415 |
| Body size | 10 MiB at most |
| Answer | The payload of the Flow’s last “Return Generic Result”, or {"completed": true} |
| Flow failed | 502; the hub answers 200 with its acknowledgement |
| Time limit | The service’s request time limit. The run is cancelled when it passes and the caller gets 502. A caller that hangs up cancels the run too |
| Busy | 429 with Retry-After: 1 while the service runs as many requests as it allows at once |
| Identity | Runs act as the person who approved the service’s cloud access, or as the device’s local user for an offline app. They carry no personal access token and no signed-in provider tokens |
| Status | Meaning on a device |
|---|---|
200 OK | The Flow finished; the body is its result |
400 Bad Request | The query or the body cannot be read |
401 Unauthorized | The service requires an access token, and the request’s token is missing or wrong |
404 Not Found | The service has no endpoint with this method and path |
408 Request Timeout | The body did not arrive within the time limit |
413 Payload Too Large | The body is larger than 10 MiB |
415 Unsupported Media Type | The body is multipart |
429 Too Many Requests | The service runs as many requests as it allows at once |
502 Bad Gateway | The Flow failed or ran past the time limit |
503 Service Unavailable | The service is stopping, or a required access token cannot be read |
Authentication
Section titled “Authentication”Bearer Token
Section titled “Bearer Token”If you configure an auth_token, all requests must include it in the Authorization header:
curl -X POST "https://your-domain.com/api/v1/sink/trigger/http/app_abc123/webhook" \ -H "Authorization: Bearer your-secret-token" \ -d '{"data": "payload"}'Requests without the correct token will receive a 401 Unauthorized response.
No Authentication
Section titled “No Authentication”If no auth_token is set, the endpoint is publicly accessible. Use this for:
- Webhooks from third-party services that can’t add custom headers
- Development/testing environments
Response Format
Section titled “Response Format”The sink waits up to 120 seconds for the Flow’s first generic_result and
returns its payload as JSON. For example, a Flow that returns
{"accepted": true} produces:
HTTP/1.1 200 OKContent-Type: application/json
{"accepted":true}The returned payload belongs to your Flow. Configure a Generic Event with a return value when the caller needs a result. Test the request above with that Event active before connecting an external service.
If execution ends without a result, the result stream fails, or the result wait expires, the hosted endpoint returns an acknowledgement:
{"triggered":true,"run_id":"run_xyz","message":"Event triggered"}The desktop endpoint instead returns the plain text Event triggered when no
result arrives. HTTP 200 alone does not prove the Flow completed successfully.
Inspect the run and its logs; do not automatically retry a request that may
already have produced effects. A reverse proxy or caller can impose a shorter
timeout than the sink’s result wait.
For progress events, use the Event invocation API:
POST /api/v1/apps/{app_id}/events/{event_id}/invoke. The SDKs expose it as
triggerEvent() and
trigger_event(). Their asynchronous
invocation methods return a run ID and poll token for longer work.
Use Cases
Section titled “Use Cases”Webhook Receiver
Section titled “Webhook Receiver”Receive webhooks from external services like GitHub, Stripe, or Slack:
{ "path": "/github-webhook", "method": "POST", "auth_token": null}API Endpoint
Section titled “API Endpoint”Create a custom API endpoint for your application:
{ "path": "/api/v1/process", "method": "POST", "auth_token": "secret-api-key"}Form Handler
Section titled “Form Handler”Handle form submissions from a website:
{ "path": "/submit-form", "method": "POST", "auth_token": null}Error Handling
Section titled “Error Handling”| Status Code | Description |
|---|---|
200 OK | Request accepted, execution started |
401 Unauthorized | Invalid or missing auth token |
404 Not Found | No matching sink found for path |
500 Internal Server Error | Execution dispatch failed |
Best Practices
Section titled “Best Practices”- Use descriptive paths:
/webhook/github/issuesis better than/hook1 - Enable authentication for production endpoints
- Use HTTPS in production for encrypted communication
- Monitor execution logs to track incoming requests
- Set up rate limiting at the infrastructure level if needed