Add a delivery URL to a SmartBrowse recipe and we’ll POST to it when a run finishes. Webhooks let you skip the poll loop entirely.
Open the recipe in the SmartBrowse studio, fill in the Webhook URL field, and (optionally) add custom request headers — for example an Authorization header your endpoint checks on receipt.
You can also limit which events fire by listing them in the recipe config. Leave the list empty to receive everything.
Event names
event is one of:
| Event | When |
|---|
completed | Run finished and items came back. |
failed | Run errored out. |
cancelled | Run was force-stopped (e.g. an account deletion confirmed mid-run). |
Payload shape
Each delivery is a POST with Content-Type: application/json and a flat JSON body — not the API envelope. Shape:
{
"event": "completed",
"timestamp": "2026-05-21T17:04:12Z",
"run_id": "k7Xb9dRmQ2p",
"recipe_id": "m3Yc2tFvN8q",
"status": "completed",
"pages_extracted": 5,
"items_extracted": 87,
"credits_used": 10,
"mode": "replay",
"drift": 0.02,
"warnings": [],
"error": ""
}
failed and cancelled deliveries use the same shape, with status set accordingly and error populated:
{
"event": "failed",
"timestamp": "2026-05-21T17:10:00Z",
"run_id": "p9Lk3vWqR7s",
"recipe_id": "m3Yc2tFvN8q",
"status": "failed",
"pages_extracted": 0,
"items_extracted": 0,
"credits_used": 0,
"mode": "",
"drift": 0,
"warnings": [],
"error": "target page did not load within timeout"
}
Authenticating deliveries
We don’t sign deliveries yet, so the burden’s on your handler to confirm the request really came from us. Two ways to do it:
- Use a hard-to-guess URL with a random path segment your endpoint expects (
/webhooks/wsg/8x4Kpz...).
- Add a custom header —
Authorization: Bearer <your-token> or anything you want — to the recipe’s webhook config, and check it on every request.
Pick one or both. Either way, verification stays in your handler.
Until we ship signed deliveries, treat the body as untrusted until you’ve validated the URL secret or header. Don’t act on run_id from an unauthenticated POST.
Delivery semantics
Every terminal event gets exactly one attempt — no automatic retries. We give your handler 10 seconds before we give up, and anything that returns a non-2xx is logged on our side but never redelivered. Treat it as fire-and-forget.
If push delivery is critical to you, treat the webhook as a hint and fall back to polling GET /v1/smartbrowse/runs/{id} on a long interval (every few minutes) to pick up anything you missed.
Testing locally
Point ngrok or webhook.site at a handler to expose it to the public internet. Trigger a run from the dashboard and watch the delivery land.