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 workflows
Set a default webhook when creating the workflow, or override it per-run:When creating a workflow, use
webhook_callback_url inside json_definition — this sets the default for all runs. When running a workflow, use webhook_url at the top level to override for that specific run.| Context | Parameter | Location |
|---|---|---|
| Task run | webhook_url | Top-level parameter |
| Workflow creation | webhook_callback_url | Inside json_definition |
| Workflow run (override) | webhook_url | Top-level parameter |
Step 2: Understand the payload
Skyvern sends a JSON payload with run results. Here’s a real example from a completed task: Webhook Payload:| Field | Type | Description |
|---|---|---|
run_id | string | Unique identifier for this run |
task_id | string | Same as run_id |
status | string | completed, failed, terminated, timed_out, or canceled |
output | object | null | Extracted data from the task. If you configured error_code_mapping, failed runs include output.error with your custom error code. |
summary | string | AI-generated description of what was done |
prompt | string | The prompt from the original request |
url | string | The URL from the original request |
downloaded_files | array | Files downloaded during execution |
recording_url | string | null | Video recording of the browser session |
screenshot_urls | array | null | Screenshots captured (latest first) |
failure_reason | string | null | Error message if the run failed |
errors | array | List of errors encountered |
step_count | integer | null | Number of steps executed |
run_type | string | Type of run: task_v2, openai_cua, anthropic_cua |
app_url | string | Link to view this run in Skyvern Cloud |
organization_id | string | Your organization ID |
workflow_run_id | string | Associated workflow run ID |
workflow_id | string | Internal workflow ID |
workflow_permanent_id | string | Permanent workflow ID used to run the workflow |
proxy_location | string | Proxy location used (e.g., RESIDENTIAL) |
webhook_callback_url | string | The webhook URL that received this payload |
webhook_failure_reason | string | null | Error message if a previous webhook delivery failed (always null in the payload you receive) |
created_at | datetime | When the run was created |
modified_at | datetime | When the run was last updated |
queued_at | datetime | null | When the run entered the queue |
started_at | datetime | null | When execution began |
finished_at | datetime | null | When execution completed |
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 payloadx-skyvern-timestamp— Unix timestamp when the webhook was sentContent-Type: application/json
Use constant-time comparison to prevent timing attacks:
- Python:
hmac.compare_digest() - TypeScript:
crypto.timingSafeEqual() - Go:
hmac.Equal()
== or ===) for signature comparison as they are vulnerable to timing attacks.Handling webhook failures
Task execution and webhook delivery are independent—a task can succeed while webhook delivery fails. When this happens, the run showsstatus: "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:
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.- 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 OKimmediately 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.
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 usingretry_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.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

