Webhooks let you receive lifecycle events from Checkr Tenant in near-real-time instead of polling the API. When a subscribed event happens, Checkr Tenant sends an HTTP POST to the URL you registered, with a JSON payload describing the event.
All webhook events are listed below. More will be added over time and your endpoints will start receiving new event types automatically. Design your handler to ignore events it doesn't recognize.
| Event | Fires when |
|---|---|
order.applicant.visited | The applicant visits the apply link for an order at any time before they complete the application. |
order.applicant.started | The applicant consents to and starts the screening application. |
order.applicant.completed | The applicant finishes and submits the application. |
report.product.completed | A product finishes processing and results are available on the report. |
report.completed | An order has finished processing and the full report is available. |
Webhook endpoints are managed from the Developer settings page in your dashboard. Each endpoint is scoped to a single organization and receives every event your organization emits — there is no per-event filtering yet.
When you create a webhook you also receive a signing secret, used to verify incoming requests. Save it somewhere durable — it is shown once at creation time and cannot be retrieved later.
Your endpoint URL must:
- Use HTTPS.
- Be reachable from the public internet. URLs pointing at
localhost, private networks, or other internal addresses are rejected.
Each webhook is registered in either live or test mode, and only receives events for resources that match its mode:
- Live — receives events for live (production) resources.
- Test — receives events for test-mode resources only.
This lets you exercise your webhook handler end-to-end against test-mode resources before pointing real production traffic at it.
Use the Test action on the dashboard to send a bare connectivity probe to your URL — a POST with no body and no signature. Your endpoint should respond with any 2xx status to indicate it is reachable. The probe is not a real event delivery and is not retried.
Each delivery is an HTTP POST from Checkr Tenant to the URL you registered:
| Field | Value |
|---|---|
| Method | POST |
Content-Type | application/json |
User-Agent | CheckrTenant/1 |
Tenant-Signature | Signature header (see below) |
A delivery is considered successful when your endpoint responds with a 2xx status code. Anything else — non-2xx responses, network errors, or timeouts — is treated as a failure and retried with exponential backoff. Your endpoint should be idempotent: the same event id may be delivered more than once.
Every webhook request body has the same envelope:
{
"id": "evt_K9dM2pXqR4vN8tLZbWyHJa",
"object": "event",
"type": "report.completed",
"created_at": "2026-04-29T17:42:13Z",
"data": {
"id": "rp_F3hQ7wEsLp2xBnVcRuMyTk",
"order_id": "ord_WHImYp5RjqUxBowWilloQ"
}
}| Field | Description |
|---|---|
id | Unique event id (evt_…). Stable across retries — use it for idempotency on your side. |
object | Always event. |
type | Event type (e.g. report.completed). See Available events. |
created_at | UTC timestamp the event was created, in ISO-8601 format. Use this for ordering — delivery order is not guaranteed. |
data | Event-specific payload. Shape varies by event type — see Data shape per event below. |
The shape of data depends on the event type:
| Event | data fields |
|---|---|
order.applicant.visited | id (applicant id), order_id |
order.applicant.started | id (applicant id), order_id |
order.applicant.completed | id (applicant id), order_id |
report.product.completed | id (product result id), report_id, product (snake case product name e.g. criminal_history) |
report.completed | id (report id), order_id |
For report events fetch the full report from GET /reports/{id} when you need its contents.
Every delivery includes a Tenant-Signature header so you can verify the request came from Checkr Tenant and was not modified in transit:
Tenant-Signature: t=1714414933,v1=8d2b...c4f1v1 is the hex-encoded HMAC-SHA256 of <t>.<raw_request_body>, computed with your endpoint's signing secret as the key. Recompute it on your side and compare against v1 using a constant-time comparison. Read the raw request body before any JSON parsing — re-serialization changes the bytes you HMAC.