Trigger a workflow with a webhook
The trigger.webhook trigger gives a workflow a unique inbound URL:
/api/hooks/<uuid>/<name>
The uuid is generated once when you pick the trigger in the editor and is
what routes the call; the <name> segment is cosmetic (rename it freely, the
URL keeps working). Any of GET/POST/PUT/PATCH/DELETE/HEAD can call it,
restricted by the trigger's configuration.
Pick the trigger and configure it
In the editor, select Webhook (URL entrante) as the trigger, then open the trigger's inspector:
- URL d'appel — the full path, with a copy button (copies the absolute URL of the console origin; the API origin serves the same path).
- Nom dans l'URL — the cosmetic second segment.
- Méthodes acceptées — check the HTTP methods to allow; none checked
means all are accepted (anything else answers
405). - Authentification —
none,basic(username/password) or a dedicated header (API key).basicandheaderreference a credential from the shared store (see Manage credentials) — secrets never live in the workflow YAML. Authentication is closed by default:noneis the only mode that opens the endpoint, so a hand-edited YAML carrying an unknown mode (bearer,Basic…) answers503(broken configuration) instead of accepting every caller. - Réponse HTTP — synchronous (default) or asynchronous, below.
- Champs typés du corps — optional, below.
Type the body (optional, recommended)
Each declared field (name, type, list, required) does three things at once:
- becomes a typed output port on the trigger card, extracted from the body (an absent optional field emits nothing — absence propagates, see Branching and absence);
- is checked at ingress: a missing required field or a type mismatch is
rejected
400before any run starts — a caller mistake never shows up as a failed run; - is documented in the generated OpenAPI (below).
Without declared fields the body stays free-form on the body port (Any).
The fixed ports method, headers, params, query and body always
exist; a declared field may not use those names.
A body over 1 MiB is refused 413 without being read to the end — the
same limit applies to every API route.
Ambient context is part of the contract
Context is established opportunistically (the trigger declares
establishes: "*"): the id field of any registered context kind found in the
body or query — patientId, encounterId, orderId, documentId with the
@posos/common kinds, or whatever kinds other plugins contribute — becomes the
run's ambient context. So whatever the workflow declares in its context list, the call
must supply — a call that omits it is refused 400, in the same pass as the
typed fields above, rather than starting a run that dies on the first node
needing the context.
Nothing guarantees it statically (the trigger cannot know what a caller will send), which is why the check lives at ingress. The editor makes the same point when you pick a sub-workflow to call: a webhook-triggered parent guarantees no context to its children.
Synchronous response (default)
The HTTP response waits for the run (up to Attente maximale, default 30 s) and answers:
| Outcome | Status | Body |
|---|---|---|
| run completed | 200 |
{ runId, status, response } |
| run failed | 502 |
{ runId, status, error, resultUrl } |
| still running past the delay, or suspended on a human validation | 202 |
{ runId, status, resultUrl } |
response is defined by the workflow's workflow.return node — the same
convention as answering a parent workflow (see
Call a sub-workflow) — so the response schema is
typed in the OpenAPI. Without a return node, the fallback is the data outputs
of the last node that emitted; a return node that is declared but never
reached answers null (the workflow chose silence).
Asynchronous mode
Switch Réponse HTTP to asynchronous to get an immediate ack
(201/200 with { runId, status, created, resultUrl }).
Either way, resultUrl — GET /api/hooks/<uuid>/runs/<runId>, same
authentication as the webhook — follows the run
({ status, progress: { done, total } } while it lives) and delivers the
response or the error once it is terminal.
Prefer asynchronous mode for anything that isn't quick: a synchronous call
holds an HTTP connection and polls the run until it settles — see
Sizing guidance, under proxy.
Replays and documentation
- Send an
X-Idempotency-Keyheader to make retries safe: the same key returns the existing run instead of starting a second one. GET /api/openapi.jsondocuments every webhook (methods, typed body, typed response, auth scheme, result endpoint). The Endpoints module in the console lists them with the same information.
Related
- Call a sub-workflow — the
workflow.returnconvention this response reuses. - Manage credentials — the
shared secret store behind
basic/headerauth. - Expose an agent as an API — the other inbound surface, for single agents rather than whole workflows.
- Core dataflow primitives — the
trigger.webhooknode descriptor.