Why async?
Generating 60 professional AI headshots takes about 1–2 hours end-to-end. Holding an HTTP connection open that long is fragile (proxies time out, clients drop, retries re-trigger work). Webhooks decouple the slow work from the request/response cycle: Runflow acknowledges the job instantly, processes it in the background, then pushes the results to you when they’re ready.Flow at a glance
- You call the async endpoint with a
callback_urlpointing at your webhook handler. - Runflow validates the request and responds immediately with a
headshots_id(or similar job ID). - You save the job ID so you can match it to the callback later.
- Runflow runs the pipeline in the background.
- Runflow POSTs the final payload to your
callback_urlwhen done. - You save the result URLs to your own storage immediately (they expire — see below) and return
200 OK.
Callback payload
For Generate Headshots, the callback body looks like:| Field | Type | Description |
|---|---|---|
event | string | Always headshots.completed for this endpoint. Use this to route on event type if you handle multiple. |
headshots_id | string (UUID) | Matches the headshots_id returned by the initial API call. |
image_urls | string[] | 60 generated headshot URLs. Expire after 24 hours. |
Handler example (Node.js / Express)
- Respond fast. Acknowledge with
200first, then do the heavy work (downloading, re-uploading to your storage) in a background job. - Be idempotent. If Runflow retries the callback, match on
headshots_idand no-op if you’ve already processed it. - Validate the source. Put your webhook behind a secret path or verify a shared header so random callers can’t spoof callbacks.
Registering your callback URL
You don’t register webhooks separately — you passcallback_url as a field on
each async endpoint call. That means you can use different callback URLs per
job (useful for multi-tenant apps where each customer has their own handler)
without any dashboard configuration.
Local development
Your laptop isn’t reachable from the public internet, so Runflow can’t POST tohttp://localhost:3000. While building, use a tunneling tool like
ngrok or Cloudflare Tunnel
to expose your local server under a public URL, then pass that URL as
callback_url.
What’s next
Generate Headshots reference
Full request/response schema and playground.
Vibe Coder Integration
Paste-ready AI-agent prompts that wire this up for you.