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.
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:
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:
{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending"
}
Payload — successful job
{
"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
{
"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
{
"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.
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 → API Keys → Webhooks.
See Signing Secrets & Verification 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:
GET /job-status/{task_id}
For workflow runs, see the status and download endpoints in Workflow Execution.