Webhooks
Get a signed POST to your server when a workflow run finishes, instead of polling.
Wireflow sends an outbound completion webhook: start a run with POST /api/v1/workflows/{id}/run, include a webhook block, and Wireflow POSTs the result to your URL when the run reaches a terminal state. Polling keeps working either way, so you can use both.
Request a Completion Webhook
Add webhook (and optionally metadata) to the run body:
curl -X POST https://www.wireflow.ai/api/v1/workflows/cm1abc123/run \
-H "Authorization: Bearer sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"inputs": { "node-prompt": "A cat wearing a top hat" },
"metadata": { "jobId": "job_42" },
"webhook": {
"url": "https://yourapp.com/api/webhooks/wireflow",
"secret": "whsec_your_signing_secret"
}
}'
| Field | Type | Required | Description |
|---|---|---|---|
webhook.url |
string |
Yes | Where to POST the result. Must be https (plain http is accepted only for localhost, 127.0.0.1 and *.localhost). Max 2048 characters. |
webhook.secret |
string |
No | Signing secret. When set, every delivery carries an HMAC signature. Recommended. |
metadata |
object |
No | Any JSON object. Echoed back verbatim in the delivery so you can match it to your own job. |
An invalid webhook block is rejected up front with 400 and code invalid_webhook, before anything runs.
The Delivery
On a terminal state Wireflow sends POST to your URL with a JSON body:
{
"event": "execution.completed",
"eventId": "550e8400-e29b-41d4-a716-446655440000",
"executionId": "exec_789",
"workflowId": "cm1abc123",
"status": "COMPLETED",
"nodeStates": [
{
"nodeId": "node-video",
"label": "Final video",
"status": "COMPLETED",
"error": null,
"outputUrl": "https://cdn.wireflow.ai/..."
}
],
"metadata": { "jobId": "job_42" },
"createdAt": "2026-09-28T12:01:15.000Z"
}
eventisexecution.completedwhenstatusisCOMPLETED, andexecution.failedforFAILED,TIMEOUTandCANCELLED. Readstatusfor the precise reason.nodeStateshas one entry per node that ran, withoutputUrl/outputUrls/text/erroras applicable.metadataappears only if you sent it.
Headers:
Content-Type: application/json
User-Agent: Wireflow-Webhook/1.0
X-Wireflow-Event: execution.completed
X-Wireflow-Delivery: <delivery id>
X-Wireflow-Signature: sha256=<hex>
X-Wireflow-Signature is present only when you set webhook.secret.
Verify the Signature
The signature is an HMAC-SHA256 of the raw request body with your secret. Verify it before trusting the payload:
import { createHmac, timingSafeEqual } from 'crypto';
function verify(rawBody: string, header: string, secret: string): boolean {
const expected = `sha256=${createHmac('sha256', secret)
.update(rawBody)
.digest('hex')}`;
const a = Buffer.from(header);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}
Delivery Guarantees
- At least once. Any
2xxresponse counts as delivered. Anything else, or no answer within 10 seconds, is retried with exponential backoff (starting at 30 seconds, capped at 6 hours) for up to 12 attempts. - One event per run. Each execution produces exactly one delivery event, whichever way the run finished. Retries of that event reuse the same
eventId, so dedupe on it. - Respond quickly and do heavy work after you return
200.
Triggering Runs From Other Systems
To start a run from Zapier, Make, n8n, a cron job or your own backend, call POST /api/v1/workflows/{id}/run with an API key. Add a webhook block if you want the result pushed back.
Inbound trigger URLs are not available. The API has a keyless trigger path (
/api/v1/workflow/{webhookId}/trigger). It needs awebhookId, which you can set throughPOSTorPUT /api/v1/workflows, plus a matchingX-Webhook-Secretheader, and there is currently no way to obtain that secret. So every call fails:429once an IP passes 100 requests an hour (checked before auth), otherwise401. A workflow that did authenticate would also be capped at 20 triggered runs an hour. UsePOST /runwith an API key instead.