Skip to main content

Webhook notifications

All async endpoints support optional webhook notifications. Instead of polling /job-status/{task_id} to check if your job is complete, you can provide a webhook_url and receive an HTTP POST request when the job finishes.

Supported endpoints

The following async endpoints support webhooks:
EndpointDescription
/find-email-asyncSingle email enrichment
/find-phone-asyncSingle phone enrichment
/find-email-bulk-asyncBulk email enrichment (up to 100 leads)
/find-phone-bulk-asyncBulk phone enrichment (up to 100 leads)
/enrich-lead-asyncLead enrichment
/enrich-company-asyncCompany enrichment
/research-agent-asyncResearch agent
/qa-agent-asyncQA agent evaluation

Usage

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"
}

Webhook payload

When your job completes, we send a POST request to your webhook URL with the following JSON payload:

Successful job

{
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "task_type": "find_email",
  "result": {
    "name": "John Doe",
    "company": "Acme Inc",
    "email": [["[email protected]", "OK"]]
  }
}

Failed job

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

Payload fields

FieldTypeDescription
task_idstringUnique identifier for the job.
statusstringEither "completed" or "failed".
task_typestringType of job (e.g., find_email, enrich_lead, find_phone_bulk).
resultobjectJob result data (only present when status is "completed").
errorstringError message (only present when status is "failed").

Retry behavior

If your webhook endpoint is unavailable or returns a non-2xx status code, we retry delivery with exponential backoff:
  • Attempts: Up to 5 retries
  • Backoff: 1s, 2s, 4s, 8s, 16s between attempts
  • Timeout: 10 seconds per attempt
If all retries fail, the webhook is marked as undelivered. The job itself remains successful and results can still be retrieved via /job-status/{task_id}.

Best practices

  1. Return 2xx quickly: Your webhook endpoint should return a 2xx status code within 10 seconds. Process the payload asynchronously if needed.
  2. Idempotency: Design your webhook handler to be idempotent. While rare, you may receive duplicate deliveries.
  3. Use HTTPS: We recommend using HTTPS URLs for webhook endpoints to ensure payload security.
  4. Validate task_id: Store the task_id from your initial request and validate it matches the webhook payload.

Example: Receiving webhooks

Node.js (Express)

app.post('/webhooks/sixtyfour', express.json(), (req, res) => {
  const { task_id, status, task_type, result, error } = req.body;
  
  if (status === 'completed') {
    console.log(`Job ${task_id} completed:`, result);
    // Process the result
  } else {
    console.error(`Job ${task_id} failed:`, error);
    // Handle the error
  }
  
  res.status(200).send('OK');
});

Python (Flask)

@app.route('/webhooks/sixtyfour', methods=['POST'])
def handle_webhook():
    data = request.json
    task_id = data['task_id']
    status = data['status']
    
    if status == 'completed':
        result = data['result']
        # Process the result
    else:
        error = data['error']
        # Handle the error
    
    return 'OK', 200

Fallback: Polling

Webhooks are optional. You can always fall back to polling /job-status/{task_id} if:
  • Your infrastructure doesn’t support incoming webhooks
  • Webhook delivery fails
  • You need to retrieve results at a later time
GET /job-status/{task_id}