Browse documentation
Guide Updated 11 August 2026

Receive webhooks

Get a webhook URL for your workflow, call it with a service account token, and understand redeliveries, limits, and sender verification.

View as Markdown

A webhook trigger gives your workflow a URL. Anything that can send an authenticated HTTP POST can start a run.

Outcome

An external system posts to your workflow's URL and gets 202 Accepted back with the id of the run it started.

Before you start

You need an active service account holding the webhooks scope, and the workflow must have a webhook trigger. See Service accounts and Choose a trigger.

1. Get the URL

Save the workflow. Until you do, the trigger inspector says Save workflow to generate webhook token — the token is minted on save.

Afterwards the inspector shows a read-only Webhook URL field. The canonical form is token-only: the token identifies the trigger, the organization, and the workspace all by itself, so nothing in the URL depends on your organization's name.

2. Call it

curl -X POST https://app.example.com/api/v1/webhooks/YOUR_TRIGGER_TOKEN \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"order_id": "A-123", "total": 250}'

$ACCESS_TOKEN is a short-lived token you exchange your service account's client ID and secret for, and it must carry the webhooks scope. Service accounts walks through the exchange.

An accepted delivery returns 202 Accepted with the id of the run it started:

{"status": "accepted", "workflow_run_id": "0198f2c1-..."}

The other answers you can get:

Status Means
401 The credential is missing or wrong.
403 It authenticated but lacks the webhooks scope.
404 No active trigger matches the token — including a workflow that is not activated.
413 The body is over 1 MB.
422 The payload could not be processed.
429 Over 100 requests per minute for this token. A Retry-After header says when to try again.

Both limits are enforced before any work is done.

3. Discover the payload shape

The steps below a trigger can only offer you fields they know about, so the inspector has an Output Schema section with a Refresh Schema button.

Press it and Hodoflow starts listening, showing a Test URL (no data saved) and Waiting for a test POST request.... Post a representative payload to that URL and the field names appear as N fields found. Nothing is stored and no run is created.

Redeliveries and duplicates

Hodoflow recognises a redelivery of something it already accepted and answers 202 with {"status": "duplicate"} instead of starting a second run. A sender retrying an acknowledgement it never saw therefore stops retrying, without double-processing.

Identity comes from whichever of these headers your sender provides: Idempotency-Key, X-GitHub-Delivery, X-Shopify-Webhook-Id, or X-Twilio-Idempotency-Token. With none of them, Hodoflow falls back to hashing the token and the raw body inside a two-hour window, so a byte-identical payload is treated as a duplicate within that window and processes normally in a later one.

Verifying the sender

A webhook trigger can also point at an inbound connector, chosen under Inbound Authentication in the inspector, to verify signatures or API keys on top of the service account's token. The picker only appears when your organization has inbound connectors that carry an authentication method — see Inbound connectors.

Common problems

Every call returns 404. The workflow is not active, or the token belongs to a version that is no longer live. Only active triggers are served. Activate the workflow and re-copy the URL.

Every call returns 401. The access token is missing, expired, or was issued from a deactivated service account. Tokens last 15 minutes; exchange again rather than caching one.

Calls return 403. The service account authenticated but does not hold the webhooks scope. Add it, then exchange for a new token — a scope change takes effect on the next exchange.

The sender says it got 202 but no run appears. Check the response body. {"status": "duplicate"} means Hodoflow matched it to a delivery it had already accepted.

429 under normal load. The cap is per trigger token, at 100 requests per minute. Split high-volume senders across triggers, or batch the payloads.

Next step

Configure and test a step to do something with what arrived. The wire-level reference — request and response bodies, every status code, header names, and the token exchange — is on the developer portal at webhook ingestion.

Related documentation