This is the complete inbound path: an identity for the caller, a workflow with a URL, and one authenticated request that produces a run you can open.
What you will build
- A service account called Webhook ingest holding only the
webhooksscope. - A workflow called Order intake with a webhook trigger that uses it.
- One accepted delivery, visible as a completed run in Monitor.
Before you start
- Creating a service account is limited to owners and admins. If New Service Account is missing, that is why.
- Nothing external needs configuring. You will send the request yourself.
1. Create the service account
Go to Settings → Service Accounts and choose New Service Account.
| Field | Value |
|---|---|
| Name | Webhook ingest |
| Scopes | webhooks only |
| Run as | Yourself, or whoever should own this integration long term |
Run as matters more than it looks. A service account has no permissions of its own — it borrows a member's. Every request authenticated with it executes as that person, so deactivating them changes what the credential can reach.
Checkpoint: the service account appears in the list as Active.
2. Copy the client ID and secret
Open the service account and find Client Credentials. You get a client ID
beginning cid_ and a client secret beginning csec_, each with a Copy
button.
Unlike most secrets, these stay retrievable — you can come back to this page whenever an integration needs reconfiguring. Access is limited to owners and admins.
Checkpoint: you have both values to hand.
3. Build the workflow
Go to Workflows → New Workflow. In the trigger wizard choose Webhook, then select Webhook ingest in the Service account picker. Press Done.
Add one step so the workflow has something to do — an HTTP Request to any endpoint you like is enough for this walk-through — and connect the trigger to it.
Name the workflow Order intake and press Save.
Checkpoint: the trigger inspector now shows a read-only Webhook URL. Before saving it said Save workflow to generate webhook token — the token is minted on save.
4. Activate it
Press Activate. Only active triggers are served: until you do this, the
URL answers 404.
Checkpoint: the workflow reads Active.
5. Exchange the credentials for a token
The client ID and secret are not what you send with the request. Exchange them for a short-lived token first:
curl -X POST https://app.example.com/api/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "cid_...",
"client_secret": "csec_..."
}'
The response is the token, its type, and its lifetime in seconds:
{"access_token": "eyJ...", "token_type": "bearer", "expires_in": 900}
Fifteen minutes. A long-lived integration exchanges again rather than caching one forever.
Checkpoint: you have an access_token.
6. Post a payload
Copy the Webhook URL from the trigger inspector and post to 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}'
An accepted delivery returns 202 Accepted with the id of the run it started:
{"status": "accepted", "workflow_run_id": "0198f2c1-..."}
Checkpoint: you got 202 and a workflow_run_id.
7. Teach the workflow the payload's shape
Steps below the trigger can only offer fields they know about. In the trigger inspector, open Output Schema and press Refresh Schema.
Hodoflow shows a Test URL (no data saved) and waits. Post the same payload to that URL, and the field names appear as N fields found. Nothing is stored and no run is created.
order_id and total are now available to every step below, as
input.order_id and input.total.
Checkpoint: 2 fields found.
Verify the result
You are done when all of these are true:
- Settings → Service Accounts lists Webhook ingest as Active with
only the
webhooksscope. - Workflows lists Order intake as Active.
- Your
curlreturned202with aworkflow_run_id. - Workflows → Monitor holds a run whose Source is that trigger.
- The trigger's Output Schema lists the fields you sent.
Common problems
401 with {"error": "invalid_client"} at the token step. Every
credential problem returns this same response — wrong secret, unknown client,
deactivated account. That is deliberate. Check the service account's status in
Hodoflow rather than reading the error.
403 when posting to the webhook. The token authenticated but does not
carry the webhooks scope. Add the scope, then exchange again — a scope change
takes effect on the next exchange, not instantly.
404 when posting to the webhook. The workflow is not active, or the URL
is from a version that is no longer live. Activate it and re-copy the URL.
202 with {"status": "duplicate"}. Hodoflow matched your request to a
delivery it already accepted, so it did not start a second run. Change the
payload, or send a different Idempotency-Key header.
The service account picker in the wizard was empty. Only active service
accounts holding webhooks are listed. Check both.
429. The cap is 100 requests per minute per trigger token.
Next steps
- Receive webhooks — redeliveries, limits, and verifying the sender.
- Persist workflow data — keep what arrives instead of discarding it.
- Naming, reuse, rotation, and deletion — before this credential is in production.