Webhooks notify your endpoint when an asynchronous job finishes, so you don’t have to poll the bulk status endpoints.

Events

EventFired when
email.find.bulk.completedA bulk email finder job finishes (or fails)
phone.find.bulk.completedA bulk phone finder job finishes (or fails)

Delivery format

Each delivery is an HTTP POST to your URL with a JSON body:
{
  "event": "email.find.bulk.completed",
  "timestamp": "2026-06-11T12:00:00.123456",
  "data": {
    "job_id": "8f14e45f-...",
    "status": "completed",
    "total": 100,
    "found": 87,
    "not_found": 13,
    "amount_charged": 1.74,
    "error": null
  }
}
data carries the job summary: counts of found / not-found items, the amount charged in USD, and error when status is error. Fetch the actual results from the bulk status endpoint using job_id. Headers sent with every delivery:
HeaderValue
Content-Typeapplication/json
X-Webhook-EventThe event type, same as event in the body
X-Webhook-TimestampDelivery timestamp, same as timestamp in the body
X-Webhook-SignatureBase64-encoded HMAC-SHA256 of the raw request body

Verifying signatures

Every webhook is signed with your webhook’s secret. If you don’t provide one at registration, a cryptographically strong secret is generated for you — it is returned by the create and get endpoints. The signature is base64(HMAC_SHA256(secret, raw_body)). Always compute it over the raw request bytes, before any JSON parsing, and compare in constant time:
import base64
import hashlib
import hmac

def verify(secret: str, raw_body: bytes, signature: str) -> bool:
    digest = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode("utf-8")
    return hmac.compare_digest(expected, signature)

# in your handler:
# verify(secret, request.body, request.headers["X-Webhook-Signature"])
Reject deliveries with a missing or invalid signature. The signature is your only guarantee the payload came from Generect.

Retries and timeouts

PolicyValue
Response timeout30 seconds
Success criteriaAny 2xx response
Your endpoint returns 4xxDelivery marked failed, no retries
5xx / network error / timeoutRetried with exponential backoff
Attempts5 total (1 initial + 4 retries)
Backoff10s → 20s → 40s → 80s between attempts
Respond 2xx immediately and process the payload asynchronously — a handler slower than 30 seconds counts as a failed attempt.

Testing

Send a test delivery to any registered webhook with POST /webhooks/{id}/test/ — it fires a webhook.test event through the same delivery pipeline, signature included.