Skip to content

Webhooks guide

Outbound webhooks let RolesAPI notify your server the moment an async job finishes, instead of you polling for status. Register an endpoint once and every job’s completion event is delivered to it, signed so you can verify it came from us.

How do I create a webhook?

Terminal window
curl -X POST "https://api.rolesapi.com/v1/webhooks" \
-H "Authorization: Bearer rk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hooks/rolesapi",
"events": ["job.succeeded", "job.failed"]
}'
{
"data": {
"id": "wh_01habc",
"url": "https://example.com/hooks/rolesapi",
"events": ["job.succeeded", "job.failed"],
"secret": "whsec_9f2c4e6a8b0d1f3a5c7e9b2d4f6a8c0e",
"active": true,
"created_at": "2026-07-01T09:15:00Z"
},
"request_id": "req_0b3d55e7"
}

The secret is shown once, in this response only. Store it immediately — later reads of the webhook return the object without the secret. If you lose it, delete the webhook and create a new one.

There are four supported events: job.succeeded, job.failed, job.timed_out, and job.aborted. A webhook receives every matching event for jobs on your account.

What does a delivery look like?

RolesAPI POSTs JSON to your URL:

{
"event": "job.succeeded",
"delivered_at": "2026-07-01T09:32:41Z",
"data": {
"job": {
"id": "5f0f6c2e-4b3a-4e9c-9c74-31d1f2a30c11",
"type": "batch_detail",
"status": "succeeded",
"result_count": 120,
"created_at": "2026-07-01T09:30:02Z",
"started_at": "2026-07-01T09:30:03Z",
"completed_at": "2026-07-01T09:32:41Z"
}
}
}

The payload carries the job, not the roles — fetch results from GET /v1/jobs/{id}/results with your API key, in any output format.

How do I verify the signature?

Every delivery includes the header:

x-rolesapi-signature: t=1751362361,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t is the Unix timestamp when the delivery was signed.
  • v1 is HMAC-SHA256(secret, "<t>.<raw body>") in hex — the timestamp, a period, and the exact raw request body.

Verification steps:

  1. Read the raw request body as bytes, before any JSON parsing or re-serialization.
  2. Parse t and v1 out of the header.
  3. Reject if |now - t| > 300 seconds — this bounds replay of captured deliveries.
  4. Compute HMAC-SHA256(secret, t + "." + raw_body) and compare with v1 using a timing-safe comparison.
  5. Only then act on the payload.

Runnable Node and Python verifiers are in Verify a webhook signature.

What if my endpoint is down?

Respond with a 2xx within a few seconds to acknowledge a delivery. Non-2xx responses and timeouts are retried with increasing delay. Every attempt is recorded — inspect them at any time:

Terminal window
curl "https://api.rolesapi.com/v1/webhooks/wh_01habc/deliveries" \
-H "Authorization: Bearer rk_live_your_key"

Each delivery record includes the event, attempt number, response status, and timestamp — see the Webhooks reference. Because retries can produce duplicate deliveries, make your handler idempotent: key on the job id and ignore events you have already processed.

How should I handle the secret?

  • Store it in a secrets manager or environment variable, never in code or logs.
  • Use a distinct endpoint (and therefore secret) per environment.
  • To rotate: create a new webhook, verify deliveries arrive, then DELETE /v1/webhooks/{id} on the old one.
RolesAPI is not affiliated with, endorsed by, or sponsored by Indeed, Inc. Indeed is a registered trademark of Indeed, Inc. RolesAPI provides access to publicly available data via a stable REST API.