Skip to main content
Agents and task runs are asynchronous. When you call run_task or run_workflow, the API returns immediately with a run ID, but the actual execution happens in the background and can take variable time. Instead of polling the get_runs endpoint, you can use Webhooks to get notified when they finish. Webhooks fire only when a run reaches a terminal status: completed, failed, terminated, timed_out, or canceled. This page covers setting them up, explains payload structure, signature verification, and handling delivery failures.

Step 1: Set webhook URL

For tasks

For agents

Set a default webhook when creating the agent, or override it per-run:
When creating an agent, use webhook_callback_url inside json_definition; this sets the default for all runs. When running an agent, use webhook_url at the top level to override for that specific run.
Quick reference:
Watch the parameter names. Using webhook_url when creating an agent (instead of webhook_callback_url inside json_definition) silently results in no webhook being sent. The API won’t return an error. Your runs will just complete without notifications.

Step 2: Understand the payload

Skyvern sends a JSON payload with run results. Here’s a real example from a completed task: Webhook Payload:
Request Headers Sent:

Optional: Verify webhook signatures

Skyvern signs every webhook with your API key using HMAC-SHA256, so you can verify the request actually came from Skyvern before acting on it. Headers sent with every webhook:
  • x-skyvern-signature: HMAC-SHA256 signature of the payload
  • x-skyvern-timestamp: Unix timestamp when the webhook was sent
  • Content-Type: application/json
Use constant-time comparison to prevent timing attacks:
  • Python: hmac.compare_digest()
  • TypeScript: crypto.timingSafeEqual()
  • Go: hmac.Equal()
Never use simple equality operators (== or ===) for signature comparison as they are vulnerable to timing attacks.
Always validate against the raw request body bytes. Skyvern normalizes JSON before signing: it removes whitespace (using compact separators) and converts whole-number floats to integers (3.0 becomes 3). If you parse the JSON and re-serialize it, the byte representation will differ and signature validation will fail.

Handling webhook failures

Task execution and webhook delivery are independent; a task can succeed while webhook delivery fails. When this happens, the run shows status: "failed" even though your data was extracted successfully. Webhook delivery can fail due to network issues, server errors, or misconfigured URLs. When this happens, the run is marked as failed and the error is recorded in the failure_reason field. Check it by calling get_run after the run terminates:
The failure_reason field contains the specific error message, for example:
Even when webhook delivery fails, the task’s output field may still contain extracted data if the browser automation completed successfully before the webhook attempt.
Common reasons webhooks fail:
  • Server unreachable: Your server is down, behind a firewall, or the URL is incorrect. Verify the URL is publicly accessible (not localhost) and check your server logs for incoming requests.
  • Timeout: Skyvern waits 10 seconds for a response. If your server takes longer, the delivery is marked as failed even if processing eventually succeeds. Return 200 OK immediately and process the payload in a background job.
  • Server returns an error: Your endpoint received the payload but responded with a non-2xx status code (e.g., 500). Check your server logs to identify the issue.
  • Signature validation fails: If your verification logic rejects the request, make sure you’re validating against the raw request body, not parsed-and-re-serialized JSON (re-serializing changes the byte representation). Also verify you’re using the same API key that created the run.
Recommended pattern: Always have a fallback polling mechanism for critical agents. If you don’t receive a webhook within your expected window, call get_run to check if the run completed and retrieve the data directly.

Retrying webhooks

Once you’ve identified and fixed the issue, you can replay the webhook using retry_run_webhook.
Skyvern does not automatically retry failed webhooks. This is intentional: automatic retries can cause duplicate processing if your server received the payload but returned an error. You must explicitly call retry_run_webhook after fixing the issue.
retry_run_webhook is fire-and-forget; it returns immediately without waiting for delivery confirmation. To verify success, monitor your webhook endpoint directly or check the run’s failure_reason field after a short delay.
Implement idempotency. If you call retry_run_webhook, you may receive the same payload twice (once from the original attempt that your server processed but returned an error, and once from the retry). Use the run_id as an idempotency key: check if you’ve already processed this run before taking action.
You can pass a different webhook_url to send the payload to a new endpoint; useful if the original URL was misconfigured.

Next steps

Error Handling

Handle failures and map custom error codes

Reliability Tips

Write robust prompts and add validation blocks