> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sixtyfour.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Outgoing Webhooks

> Receive HTTP notifications from Sixtyfour when async jobs and workflows complete.

## Use Case

Sixtyfour POSTs to your URL when work completes — so you can react in real time instead of polling. Outgoing webhooks come from two sources, both delivered through the same signed, retried HTTP pipeline:

| Source                                | Trigger                                             | Payload                            |
| ------------------------------------- | --------------------------------------------------- | ---------------------------------- |
| **Async-job webhook**                 | Pass `webhook_url` in the body of an async endpoint | Job result envelope                |
| **Workflow `outgoing_webhook` block** | Add the block to a workflow                         | Workflow run envelope with results |

Headers, signing, retry behavior, and verification are identical for both sources — only the payload shape differs.

## Source 1: Async-job webhooks

All async enrichment endpoints accept an optional `webhook_url` parameter. When the job finishes, Sixtyfour POSTs the result to that URL instead of requiring you to poll `/job-status/{task_id}`.

### Supported endpoints

| Endpoint                      | Description                             |
| ----------------------------- | --------------------------------------- |
| `/find-email-async`           | Single email enrichment                 |
| `/find-phone-async`           | Single phone enrichment                 |
| `/find-email-bulk-async`      | Bulk email enrichment (up to 100 leads) |
| `/find-phone-bulk-async`      | Bulk phone enrichment (up to 100 leads) |
| `/people-intelligence-async`  | People intelligence                     |
| `/company-intelligence-async` | Company intelligence                    |
| `/research-agent-async`       | Research agent                          |
| `/qa-agent-async`             | QA agent evaluation                     |

### Request

Add the optional `webhook_url` parameter to your async request:

```json theme={null}
POST /find-email-async
{
  "lead": {
    "name": "John Doe",
    "company": "Acme Inc",
    "linkedin_url": "https://linkedin.com/in/johndoe"
  },
  "webhook_url": "https://your-server.com/webhooks/sixtyfour"
}
```

The async endpoint immediately returns a `task_id`:

```json theme={null}
{
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending"
}
```

### Payload — successful job

```json theme={null}
{
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "task_type": "find_email",
  "result": {
    "name": "John Doe",
    "company": "Acme Inc",
    "email": [["john.doe@acme.com", "OK"]]
  }
}
```

### Payload — failed job

```json theme={null}
{
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "failed",
  "task_type": "find_email",
  "error": "Error message describing what went wrong"
}
```

### Payload fields

| Field       | Type   | Description                                                         |
| ----------- | ------ | ------------------------------------------------------------------- |
| `task_id`   | string | Unique identifier for the job.                                      |
| `status`    | string | Either `"completed"` or `"failed"`.                                 |
| `task_type` | string | Type of job (e.g., `find_email`, `enrich_lead`, `find_phone_bulk`). |
| `result`    | object | Job result data (only present when `status` is `"completed"`).      |
| `error`     | string | Error message (only present when `status` is `"failed"`).           |

## Source 2: Workflow `outgoing_webhook` block

Add an `outgoing_webhook` block to a workflow to POST the full run result to an external URL when the workflow completes. One request per workflow run, not per row.

### Block specs

| Field                       | Type    | Default  | Description                                                                                                                |
| --------------------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| `url`                       | string  | required | Destination URL. Validated against SSRF rules — must be a public host.                                                     |
| `headers`                   | object  | none     | Optional HTTP headers to include in the request. Header names are sanitized (CR/LF stripped).                              |
| `timeout_seconds`           | float   | `10`     | Per-attempt request timeout. Range `0 < x ≤ 300`. Internally capped at 150s to fit the activity heartbeat window.          |
| `include_csv_download_link` | boolean | `false`  | If true, waits up to 120s for the previous block's CSV result and includes a signed `results_download_url` in the payload. |

The block also adds three columns to the workflow output dataset: `webhook_status_code`, `webhook_response`, and `webhook_success`.

### Payload envelope

```json theme={null}
{
  "event_id": "f7c2a4e0-9b5d-4a1f-8d2c-1e3b5f7a9c0d",
  "event": "outgoing_webhook.completed",
  "status": "success",
  "workflow_id": "wf_abc123",
  "run_id": "run_xyz789",
  "organization_id": "org_456",
  "row_count": 95,
  "previous_block_id": "blk_2",
  "previous_block_name": "lead_enrichment",
  "previous_block_number": 2,
  "completed_at": "2026-04-22T20:00:00Z",
  "duration_ms": 1234,
  "attempt": 1,
  "results_download_url": "https://...signed-csv-url...",
  "results": [
    { "name": "John Doe", "email": "john@acme.com" }
  ]
}
```

### Payload fields

| Field                   | Type                | Description                                                                                                                       |
| ----------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `event_id`              | string (UUID)       | Unique per delivery. Use as your dedupe key — same as the `Sixtyfour-Event-Id` header.                                            |
| `event`                 | string              | Always `outgoing_webhook.completed`.                                                                                              |
| `status`                | string              | Always `success` for a delivered webhook. Failure is signaled by HTTP status, not this field.                                     |
| `workflow_id`           | string              | The workflow definition ID.                                                                                                       |
| `run_id`                | string              | The specific run ID.                                                                                                              |
| `organization_id`       | string              | Your organization ID.                                                                                                             |
| `row_count`             | integer             | Number of rows in the result.                                                                                                     |
| `previous_block_id`     | string              | ID of the block immediately before the webhook block.                                                                             |
| `previous_block_name`   | string              | Block type of the previous block (e.g. `lead_enrichment`).                                                                        |
| `previous_block_number` | integer             | Sequence number of the previous block.                                                                                            |
| `completed_at`          | string (ISO 8601 Z) | When the webhook was sent.                                                                                                        |
| `duration_ms`           | integer             | How long the webhook block took.                                                                                                  |
| `attempt`               | integer             | Delivery attempt counter (starts at 1, increments on retry).                                                                      |
| `results_download_url`  | string \| null      | Signed CSV download URL when `include_csv_download_link=true`. Expires per signing policy.                                        |
| `results`               | array \| null       | Inline result rows. Present when the result is small enough to embed; otherwise null and `results_download_url` carries the data. |

## Shared delivery infrastructure

Everything below applies to **both** sources above.

### Headers

When your org has a signing secret configured, every delivery includes:

| Header                       | Value                                                           |
| ---------------------------- | --------------------------------------------------------------- |
| `Sixtyfour-Signature`        | `t=<unix_seconds>,v1=<hex>[,v1=<hex>]`                          |
| `Sixtyfour-Event-Id`         | UUID per delivery — use as your dedupe key                      |
| `Sixtyfour-Event-Type`       | Routing hint (e.g., `find_email`, `outgoing_webhook.completed`) |
| `Sixtyfour-Delivery-Attempt` | Attempt counter starting at `1`; increments on each retry       |

Until you create a signing secret, deliveries are sent unsigned for backward compatibility.

### Signing and verification

One signing secret per organization covers all outgoing webhook traffic — async-job webhooks and `outgoing_webhook` blocks alike. Generate it in the dashboard at [Settings → Webhooks](https://app.sixtyfour.ai/settings/webhooks).

See [Signing Secrets & Verification](/api-reference/webhooks/signing-secrets) for the full HMAC-SHA256 verification algorithm, working Python (Flask) and Node.js (Express) code samples, rotation behavior, and fail-closed semantics.

### Retry behavior

If your endpoint is unavailable or returns a non-2xx status code, we retry delivery with exponential backoff:

| Setting  | Value                                                                                                        |
| -------- | ------------------------------------------------------------------------------------------------------------ |
| Attempts | Up to 5 retries                                                                                              |
| Backoff  | 1s, 2s, 4s, 8s, 16s between attempts                                                                         |
| Timeout  | 10 seconds per attempt (async-job webhooks); configurable per `outgoing_webhook` block via `timeout_seconds` |

If all retries fail, the webhook is marked as undelivered. The job or workflow itself remains successful and results can still be retrieved via the polling endpoints.

### Best practices

* Return a 2xx status code within 10 seconds. Process the payload asynchronously if needed — returning non-2xx triggers retries even for events you intend to ignore.
* Dedupe with `Sixtyfour-Event-Id`. Retries can cause the same event to arrive more than once, so treat the event ID as your idempotency key.
* Use HTTPS endpoints for payload security. The `outgoing_webhook` block enforces this through its URL validation.
* Match incoming deliveries against the `task_id` or `run_id` you initiated and drop unexpected payloads silently with a 2xx response.

## Fallback: polling

Webhooks are optional. You can always poll for results if:

* Your infrastructure doesn't support inbound webhooks
* Webhook delivery fails after all retries
* You need to retrieve results at a later time

For async jobs:

```bash theme={null}
GET /job-status/{task_id}
```

For workflow runs, see the status and download endpoints in [Workflow Execution](/api-reference/workflows/workflows-execution).
