This feature is currently in beta and requires approval.

Overview

Webhooks allow you to receive real-time notifications when your Parallel Task Runs complete, eliminating the need for constant polling—especially for long-running or research-intensive tasks. Our webhooks follow standard webhook conventions to ensure security and interoperability. Once webhooks are enabled for your account, you can securely send notifications to any URL of your choice.

Setup

1. Contact Support

Email support@parallel.ai to enable webhooks for your account.

2. Receive Account Secret

Upon approval, you will receive a unique account secret via email. This secret is used to sign webhook requests for security purposes.

3. Configure Webhook in Request

Webhooks must be configured on a per-run basis. To register a callback for a task run, include a webhook object in your task-run creation request:
curl --request POST \
  --url https://api.parallel.ai/v1beta/tasks/runs \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: YOUR_API_KEY' \
  --data '{
    "task_spec": {
      "output_schema": "Find the GDP of the specified country and year"
    },
    "input": "France (2023)",
    "processor": "core",
    "metadata": {
      "key": "value"
    },
    "webhook": {
      "url": "https://your-domain.com/webhooks/parallel",
      "event_types": ["task_run.status"],
      "secret": "your-optional-custom-secret"
    }
  }'

Webhook Parameters

ParameterTypeRequiredDescription
urlstringYesYour webhook endpoint URL. Can be any domain.
event_typesarray[string]YesCurrently only ["task_run.status"] is supported.
secretstringNoOptional custom secret. If not provided, your account default secret will be used.

Webhook Events

Task Run Status

We currently only support task_run.status event on task run completions. When a task run finishes, either with a success or a failure, we will send a POST request to your configured webhook endpoint.

Response Format

Request Headers

Your webhook endpoint will receive requests with these headers:
  • webhook-id: Unique identifier for each webhook event
  • webhook-timestamp: Unix timestamp in seconds
  • webhook-signature: Versioned signature, e.g. v1,<base64 signature>
{
  "Content-Type": "application/json",
  "webhook-id": "whevent_abc123def456",
  "webhook-timestamp": "1751498975",
  "webhook-signature": "v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4="
}

Webhook Payload Structure

Each webhook payload contains the following fields:
  • timestamp: ISO 8601 timestamp of when the event occurred
  • type: Event type (currently only task_run.status is supported)
  • data: Event-specific payload. For the ‘task_run.status’ event, it is the complete Task Run object

Example Payloads

The following examples demonstrate the payload structure for completed and failed task runs:
Success
{
  "timestamp": "2025-04-23T20:21:48.037943Z",
  "type": "task_run.status",
  "data": {
    "run_id": "trun_9907962f83aa4d9d98fd7f4bf745d654",
    "status": "completed",
    "is_active": false,
    "warnings": null,
    "error": null,
    "processor": "core",
    "metadata": {
      "key": "value"
    },
    "created_at": "2025-04-23T20:21:48.037943Z",
    "modified_at": "2025-04-23T20:21:48.037943Z"
  }
}
Failure
{
  "timestamp": "2025-04-23T20:21:48.037943Z",
  "type": "task_run.status",
  "data": {
    "run_id": "trun_9907962f83aa4d9d98fd7f4bf745d654",
    "status": "failed",
    "is_active": false,
    "warnings": null,
    "error": {
      "message": "Task execution failed",
      "details": "Additional error details"
    },
    "processor": "core",
    "metadata": {
      "key": "value"
    },
    "created_at": "2025-04-23T20:21:48.037943Z",
    "modified_at": "2025-04-23T20:21:48.037943Z"
  }
}

Security & Reliability

HMAC Signature Verification

Webhook requests are signed using HMAC-SHA256 with base64url encoding (URL-safe base64 without padding). The signature header is formatted as v1,<base64url signature> where <base64url signature> is signed with the following payload:
<webhook-id>.<webhook-timestamp>.<payload>
Where:
  • <webhook-id>: The value of the webhook-id header
  • <webhook-timestamp>: The value of the webhook-timestamp header
  • <payload>: The exact JSON body of the webhook request
Important: Our signatures use base64url encoding (URL-safe base64 without padding), which means:
  • Uses - and _ instead of + and / for URL safety
  • Removes padding (=) characters
  • This format is safe for use in URLs and HTTP headers
You must parse the version and the signature before verifying. The webhook-signature header may contain multiple signatures separated by spaces, so you should check all of them. Here’s how to verify the signature:
import base64
import hashlib
import hmac

def verify_webhook_signature(
    body: bytes,
    secret: str,
    header_signature: str,
    webhook_id: str,
    webhook_timestamp: str,
) -> bool:
    """Verify a webhook signature.

    Args:
        body: The raw request body bytes
        secret: The webhook secret key
        header_signature: The signature from the webhook-signature header
        webhook_id: The webhook ID from the webhook-id header
        webhook_timestamp: The timestamp from the webhook-timestamp header

    Returns:
        True if signature is valid, False otherwise
    """
    # Create the payload string: webhook_id.timestamp.body
    payload = f"{webhook_id}.{webhook_timestamp}.{body.decode()}"

    # Compute HMAC-SHA256 signature
    computed_signature = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).digest()

    # Convert to base64url format (URL-safe base64 without padding)
    digest_b64_urlsafe = base64.urlsafe_b64encode(computed_signature).decode().rstrip("=")

    # Compare signatures using constant-time comparison
    return hmac.compare_digest(digest_b64_urlsafe, header_signature)

# Example usage with multiple signatures
def verify_webhook_signatures(
    body: bytes,
    secret: str,
    webhook_signature: str,
    webhook_id: str,
    webhook_timestamp: str,
) -> bool:
    """Verify webhook signatures, handling multiple signatures in header.

    Args:
        body: The raw request body bytes
        secret: The webhook secret key
        webhook_signature: The webhook-signature header value
        webhook_id: The webhook ID from the webhook-id header
        webhook_timestamp: The timestamp from the webhook-timestamp header

    Returns:
        True if any signature is valid, False otherwise
    """
    # Extract signature from header (remove "v1," prefix if present)
    header_signatures = [
        signature.split(",", 1)[-1] for signature in webhook_signature.split()
    ]

    signature_valid = any(
        verify_webhook_signature(
            body,
            secret,
            header_signature,
            webhook_id,
            webhook_timestamp,
        )
        for header_signature in header_signatures
    )

    return signature_valid

# Usage example
webhook_id = "whevent_2KWPBgLlAfxdpx2AI54pPJ85f4W"
webhook_timestamp = "1674087231"
body = b'{"type":"task_run.status","timestamp":"2022-11-03T20:26:10.344522Z","data":{"run_id":"trun_123"}}'
secret = "your-secret-key"
header_sig = "v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4="

if verify_webhook_signatures(body, secret, header_sig, webhook_id, webhook_timestamp):
    print("✅ Signature verification successful")
else:
    print("❌ Signature verification failed")

Retry Policy

Webhook delivery uses the following retry configuration:
  • Initial delay: 5 seconds
  • Backoff strategy: Exponential backoff (doubles per failed request)
  • Maximum retries: Multiple attempts over 48 hours
After exhausting all retry attempts, webhook delivery for that event is terminated.

Best Practices

1. Always Return 2xx Status

Your webhook endpoint should return a 2xx HTTP status code to acknowledge receipt. Any other status code will trigger retries.

2. Verify Signatures

Always verify HMAC signatures using the correct secret to ensure webhook authenticity. Ensure that you are calculating signatures using the proper process as shown above. Remember that if you don’t provide a secret in your request, the default secret is always used.

3. Handle Duplicates

Although not common, duplicate events may be sent to the configured webhook URL. Ensure your webhook handler can detect and safely ignore duplicate events.

4. Process Asynchronously

Process webhook events asynchronously to avoid timeouts and ensure quick response times.