Managing webhook configs and deliveries

Set up where Check sends webhooks and observe what was sent

Two API resources let you operate your webhook integration end to end:

  • Webhook configs define where Check sends webhooks — the endpoints you register, each with a signing key.
  • Webhook deliveries record what Check sent — every webhook we generated for your provider, its delivery status, and the attempts we made to deliver it.

Together they answer the two operational questions every webhook integration eventually hits: "is Check sending to the right place?" and "did the webhook I expected actually get delivered — and if not, why?"

Both objects are also available through the Check MCP server, so you — or an AI coding agent — can inspect webhook configs and query deliveries directly from your editor or terminal while debugging an integration.

Setting up a webhook config

Register an endpoint with POST /webhook_configs:

curl https://api.checkhq.com/webhook_configs \
  -X POST \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/check/webhooks"}'

The response includes a generated key. Check signs every webhook request with it, and your handler should recompute the signature to verify authenticity — see The Shape of Your Webhook Endpoint for the verification recipe, along with retry, ordering, and idempotency guidance.

A few things to know about configs:

  • Multiple configs are supported. Every active config receives every webhook, so you can register a second endpoint to cut over gradually or to fan out to a separate consumer.
  • active controls sending. Set it to false with PATCH /webhook_configs/{id} to pause sending without losing the config (or its key). Note that webhooks generated while a config is inactive are not sent later — pausing means missing, not queueing.
  • Check may disable a dead endpoint. If an endpoint does nothing but fail for an extended period, Check can mark the config inactive. Monitor your delivery statuses to catch problems before that happens.

To confirm an endpoint is wired up, send a test webhook with POST /webhook_configs/{id}/ping. It delivers a ping-topic webhook through the same pipeline as real traffic, so it exercises your signature verification too.

Observing deliveries

GET /webhook_deliveries returns every webhook Check generated for your provider, newest first, with cursor-based pagination (default page size 25, maximum 100):

{
  "id": "whe_7uCODm8JFmZyov9jL8QO",
  "webhook_config": "whc_7uCODm8JFmZyov9jL8QO",
  "company": "com_zH8Gq33iBkFj5o0GKjTw",
  "topic": "payroll",
  "status": "delivered",
  "created_at": "2024-01-19T17:04:15Z",
  "attempts": [
    { "status_code": 200, "created_at": "2024-01-19T17:04:15Z" }
  ]
}

Filters combine with AND:

  • webhook_config — deliveries sent to one of your configs.
  • company — deliveries associated with a company.
  • topic — a webhook topic; an invalid topic returns a validation error rather than an empty list.
  • status — one of the delivery statuses below.
  • created_after / created_before — an ISO-8601 time range.

For example, to find this morning's failed payroll webhooks:

curl -G https://api.checkhq.com/webhook_deliveries \
  -H "Authorization: Bearer <API_KEY>" \
  --data-urlencode "topic=payroll" \
  --data-urlencode "status=failed" \
  --data-urlencode "created_after=2026-08-11T00:00:00Z"

Deliveries intentionally do not include the webhook payload. The payload's home is the webhook request itself; when you need current state, fetch the resource — the payload reflects the resource at generation time and may already be stale by the time you read it (see Working with Webhooks).

Understanding delivery status

Each delivery's status is derived from the delivery attempts made so far:

StatusMeaning
pendingGenerated, first attempt not yet made.
retryingAt least one attempt failed; Check is still retrying on an exponential backoff schedule.
deliveredYour endpoint returned a 2xx.
failedAll attempts were exhausted without a 2xx.

The embedded attempts array (newest first) shows each attempt's status_code — the HTTP status your endpoint returned. A null status_code means no HTTP response was received at all: the request timed out, TLS negotiation failed, or the connection could not be established. A long run of null attempts usually means your endpoint is unreachable rather than erroring.

Debugging a webhook problem

A typical investigation, using only these two APIs:

  1. Did Check generate the webhook? List deliveries filtered by topic (and company if relevant) around the expected time. No delivery means the event didn't occur — check the resource itself.
  2. Was it delivered? Look at status. A delivered webhook that your system didn't process points at your handler (check your own request logs for the delivery's id — it's sent in the Check-WebhookEvent-ID header).
  3. Why did it fail? Read attempts. Consistent 5xxs are your handler erroring; 401/403 suggests a signature or auth mismatch; null status codes mean Check couldn't reach the endpoint at all.
  4. Re-test after fixing. In the sandbox environment, re-enqueue a delivery with POST /webhook_deliveries/{id}/retry — it requires an X-Idempotency-Key header and an active webhook config, and returns a 400 in the live environment. In production, Check's automatic retry schedule handles redelivery; once your endpoint recovers, retrying deliveries will drain on their own.

Did this page help you?