Skip to content
Webhooks

Webhooks

If you pass a webhook_url to Submit, the server sends exactly one signed POST when all lines in the batch have finished enriching.

Delivery guarantees

  • Completion is detected on the fly, without blocking enrichment.
  • Delivery runs through a dedicated Temporal workflow. It is deduplicated (a single webhook is sent per project, even under concurrent completions) and retried with backoff.
  • After retries are exhausted, the webhook is marked failed and remains inspectable.

Payload

The body is minimal:

{
  "project_id": "...",
  "event": "enrichment.completed",
  "status": "completed",
  "counts": { "total": 100, "enriched": 100, "failed": 0 }
}

The webhook is a notification only. It does not carry the enriched data: after receiving it, the client must still call ExportCSV (see Project Lifecycle) to retrieve the results.

Verifying the signature

Each delivery is signed with a secret specific to your organization:

  • Header X-Dango-Signature: sha256=... is the HMAC-SHA256 of your organization’s webhook secret over the string "{timestamp}.{body}".
  • The same timestamp is also sent in the X-Dango-Timestamp header, which guards against replay attacks.

To verify, recompute the HMAC-SHA256 over "{X-Dango-Timestamp}.{raw body}" using your webhook secret and compare it against the X-Dango-Signature header.

SSRF protection. Because the webhook URL is client-provided, delivery refuses to connect to loopback, private, or link-local addresses. The check runs on the resolved IP, which also defeats DNS rebinding. A webhook_url therefore cannot be used to reach 169.254.169.254, localhost, or the internal network.