Documentation / Conception / Guides pratiques / Trigger a workflow with a webhook

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).
  • Authentificationnone, basic (username/password) or a dedicated header (API key). basic and header reference a credential from the shared store (see Manage credentials) — secrets never live in the workflow YAML. Authentication is closed by default: none is the only mode that opens the endpoint, so a hand-edited YAML carrying an unknown mode (bearer, Basic…) answers 503 (broken configuration) instead of accepting every caller.
  • Réponse HTTP — synchronous (default) or asynchronous, below.
  • Champs typés du corps — optional, below.

Each declared field (name, type, list, required) does three things at once:

  1. 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);
  2. is checked at ingress: a missing required field or a type mismatch is rejected 400 before any run starts — a caller mistake never shows up as a failed run;
  3. 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, resultUrlGET /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-Key header to make retries safe: the same key returns the existing run instead of starting a second one.
  • GET /api/openapi.json documents every webhook (methods, typed body, typed response, auth scheme, result endpoint). The Endpoints module in the console lists them with the same information.
75 documents7 sectionssource : /docs · généré au build