Build

Incoming webhooks

A webhook trigger gives the workflow a private address. Your system POSTs JSON to it with a secret, Filmo answers 202 straight away, and a run starts.

  • Choose «Webhook» as the trigger. Under «Body», declare the type of the JSON you will send.
  • In the trigger's side panel you will find «Endpoint» and «Secret», each with a copy button. They are shown in clear text, and they exist from the moment the workflow is created.
  • Publish the workflow and switch it «Live». The address answers 404 until then, and answers 404 again if the workflow is paused or the trigger is changed to another type.
A delivery (fictitious data)
curl -sS -X POST "https://app.filmo.io/api/webhooks/workflows/whk_<token>" \
  -H "X-Webhook-Secret: whsec_<secret>" \
  -H "Idempotency-Key: order-2026-0042-reserved" \
  -H "Content-Type: application/json" \
  -d '{"orderNumber":"P-2026-0042","customerEmail":"ana@example.com"}'
  • Only POST is accepted. The token is part of the address (whk_ followed by random characters), and the secret (whsec_…) goes in the header X-Webhook-Secret or as Authorization: Bearer <secret>. If both are present, X-Webhook-Secret is used. The secret is required on every call. It is a shared secret, compared in constant time; it is not a signature of the body, so a captured request can be replayed.
  • The body is parsed as JSON whatever the Content-Type says. Send a JSON object (a list or a text also work).
  • The body can be at most 256 KiB.
  • The address of the workflow in your Filmo app is the host of that app followed by /api/webhooks/workflows/ and the token.
StatusBodyWhen
202{ "runId": "…" }A run was queued.
200{ "runId": "…", "duplicate": true }You repeated an Idempotency-Key already seen on this workflow: no new run, the id of the existing one.
400{ "error": "invalid_json" }The body is empty or is not JSON.
400{ "error": "idempotency_key_too_long" }The key is longer than 255 characters. It is refused, not truncated.
401{ "error": "unauthorized" }The secret is missing or wrong.
404{ "error": "not_found" }Unknown token, the trigger is not a webhook, the workflow is not live, or it is paused. They are all the same answer on purpose.
409{ "error": "no_version" }The workflow has never been published.
409{ "error": "conflict" }A rare race with a duplicate whose run has disappeared.
413{ "error": "body_too_large" }Over 256 KiB.
429A message and retryAfterMs, with a Retry-After headerMore than about 60 requests in a minute to this address. The count is kept per server instance, so it is approximate.

Any other method answers 405.

A 202 does not mean the body was valid

Filmo checks the body against the type you declared when the run starts, not when the request arrives. If the body does not match, you have already received 202 and the run fails with a message such as "the webhook body does not match the declared type: …". The sender never finds out. Open the run in the «Runs» to see why.

The rules: extra fields are ignored; every field you declared is required (declare a field as Unknown to accept anything); a number must be a JSON number, not a text; a date must be ISO 8601; an enumeration must be one of its options. The variable trigger_body holds the body as you sent it, including the fields you did not declare.

  • Send an Idempotency-Key that identifies the thing that happened (an order number and what happened to it), not the attempt. A redelivery with the same key returns 200 and the original run. The key is unique per workflow, not across workflows.
  • Without the header, nothing is deduplicated: two identical deliveries start two runs, because two identical bodies can be two real events. Filmo never uses the body to decide.
  • The protection lasts as long as the run exists. Finished runs are deleted 90 days after they end.
  • The token and the secret are created with the workflow, for every workflow, even if it never uses a webhook. They do not belong to a version: they do not change when you save, publish, change the trigger or restore anything.
  • There is no way to rotate or regenerate them. If a secret leaks, the remedy is to build a new workflow, which gets new credentials.
  • They are shown to everyone who can open the workflow in the editor. For a «Public» workflow that means any member of your company. If that is not what you want, switch the workflow to «Private»: only its creator and administrators can then open it.

The run starts with the published version, executes with the permissions of the workflow's creator, and sees the body in trigger_body. Delivery is at least once for the effects of its steps: see Errors and retries.

Tip:

To see it end to end, the guide Start a workflow from your own system sets up a webhook, a type and a test delivery.

  • Build

    Triggers

    A trigger decides when a workflow runs. Every workflow has exactly one, and you can change it in the editor at any time (until you publish, nothing changes for the runs).

  • Build

    HTTP requests

    The HTTP step calls a URL from a run: to push a change to your own system, or to read from one. It is deliberately simple, and the simplicity has consequences you should know before you build on it.

  • Run

    Workflow permissions

    Two questions get mixed up: who may manage a workflow, and what a run is allowed to do. The first is about administrators. The second is about one person: the creator.

Was this page helpful?