Buni.aiDocs
Early accessAutomation

Inbound webhooks

Start an automation when any service sends an HTTP request to its Webhook trigger URL, with optional HMAC signing and de-duplication.

A Webhook trigger gives an automation its own URL. Any system that can make an HTTP request (your backend, a form tool, a payment provider) can start the workflow by calling it. No API token is needed: the unguessable token in the URL is the credential, and you can add a signing secret on top.

Where to find it

Drag Webhook Trigger from the Triggers group of an automation project's node palette. To start runs from your own code with an API token instead, use the automation trigger API.

Webhook URL or API token?

Webhook trigger URLAutomation trigger API
Addresshttps://www.buni.ai/api/hooks/t/{endpointToken}https://www.buni.ai/api/v1/orgs/{orgId}/automations/{projectId}/trigger
CredentialToken in the URL, optional signatureAuthorization: Bearer bai_ext_...
Best forThird-party services that only let you paste a URLYour own code
Test and productionSeparate URLsruntimeStage field
Scheduling, callbacksNorunAt, delaySeconds, webhookUrl

Set up a Webhook trigger

Add the trigger

Open the automation, add Webhook Trigger from the Triggers group of the palette, and open it.

Copy the URLs

The Endpoint section shows two URLs:

  • Test runs the draft workflow straight away. Use it while you build.
  • Prod runs the published workflow. It answers 409 until the workflow is published.

Both stay the same when you edit or republish. Treat them as secrets: anyone with the URL can start the workflow.

Screenshot pending: automation-webhook-trigger-endpoint

Choose methods and response

SettingOptions
Allowed methodsPOST by default. Add GET, PUT, PATCH or DELETE if the caller needs them. Other methods get 405.
Expected payloadOptional JSON example of what callers send. It powers the run panel's payload form.
ModeAsync (202) answers immediately. Sync (wait for result) waits for the run and returns its result.
Custom status codeOptional. Replaces the default status in the response.

Select Generate signing secret and copy the secret. It is shown once. From then on every request must be signed, and the trigger shows Signed instead of Unverified. Select Rotate signing secret to replace it; the old secret stops working at once.

Test it

Use Send test event in the trigger panel, or call the Test URL yourself. Recent deliveries lists each request and the run it started.

Call the URL

The request body becomes the workflow's input. JSON and form-encoded bodies are parsed; any other body arrives as rawBody text. Query string parameters arrive under __query.

curl -X POST "https://www.buni.ai/api/hooks/t/$ENDPOINT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "x-idempotency-key: order-12345-created" \
  -d '{"orderId": "ord_12345", "amount": 4900, "currency": "USD"}'

Sign requests

Once the trigger has a secret, send x-buni-signature with every request:

x-buni-signature: sha256=<hex HMAC-SHA256 of the raw body, keyed with the secret>

Bare hex without sha256= is also accepted. Sign the exact bytes you send.

For replay protection, also send x-buni-timestamp with the current Unix time in seconds. The signature must then cover <timestamp>.<raw body>, and requests more than 5 minutes older or newer than the server's clock are refused.

BODY='{"orderId":"ord_12345","amount":4900}'
TS=$(date +%s)
SIG=$(printf '%s' "$TS.$BODY" | openssl dgst -sha256 -hmac "$BUNI_HOOK_SECRET" -hex | sed 's/^.* //')

curl -X POST "https://www.buni.ai/api/hooks/t/$ENDPOINT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "x-buni-timestamp: $TS" \
  -H "x-buni-signature: sha256=$SIG" \
  --data-raw "$BODY"

Without a timestamp, sign only the raw body: HMAC-SHA256(secret, body).

De-duplication

Send x-idempotency-key (up to 128 characters). A repeated key is acknowledged with 200 {"deduplicated": true, "eventId": "..."} and does not start another run. Without the header, two identical requests both run.

Responses

StatusBodyMeaning
202{"accepted": true, "invocationId": "..."}Accepted in Async (202) mode.
200{"invocationId": "...", "status": "SUCCEEDED"}Sync mode, run succeeded.
500{"invocationId": "...", "status": "FAILED", "error": "..."}Sync mode, run failed.
200{"deduplicated": true, "eventId": "..."}Repeat of an earlier x-idempotency-key. Nothing ran.
200{"ignored": true, "event": "..."}App triggers only: an event type the trigger did not select.
401Signature verification failedMissing or wrong x-buni-signature, or a timestamp outside 5 minutes.
404Unknown trigger endpointNo trigger has this token. Copy the URL again from the trigger's Endpoint section.
405Method GET not allowed for this triggerMethod not in Allowed methods. The Allow header lists the allowed ones.
409This trigger is pausedThe trigger is paused.
409The workflow is not published; use the test URL or publish itProd URL called before publishing.
410This trigger was removed from its workflow...The trigger node was deleted. Re-add it, or use the new trigger's URL.
413Payload exceeds the 1048576-byte limitBody over 1 MB.
429Rate limit exceededToo many requests. See Limits.

A Custom status code replaces 202, 200 or 500 above.

Limits

  • Body size: 1 MB.
  • Each URL accepts 120 requests per minute by default, and all Webhook trigger URLs in your organization together accept five times that. Over the limit you get 429 with X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset.
  • Runs started from the URL count toward your plan's monthly API call quota. See Rate limits and quotas.

Troubleshooting

Last reviewed 25 September 2026

On this page