Batch completed

Webhook sent when an asynchronous batch reaches a final state.

Event: batch.completed

Sent when a batch submitted through POST /api/v1/batches reaches a final state — whether it succeeded or not. The body carries counters, never player profiles: read those with GET /api/v1/batches/{batch_id}.

Full request

This is a complete delivery as it arrives at your endpoint. Note that the signature is an HTTP header, not a field in the JSON body.

POST /webhooks/kyg HTTP/1.1
Host: your-domain.com
Content-Type: application/json
Content-Length: 191
X-KYG-Event: batch.completed
X-KYG-Attempt: 1
x-paag-webhook-signature: ODdmYTE0MzViMGY0ODZlYjdlMTc4N2FmMjc3YTk2NjllN2E0ZjAxZjg5NTk3ZjlmOTQ3MTAyOTg0YzA1ODdlNA==

{"event": "batch.completed", "batch_id": "lote_3c6e0b8a9c15", "status": "completed", "total_requested": 1000, "total_found": 999, "total_not_found": 1, "completed_at": "2026-08-14T16:05:34Z"}

Headers

HeaderValueWhat it is for
x-paag-webhook-signatureBase64 (88 chars)Proof the request came from us. Same header and scheme as the Payments API. See Signature verification
X-KYG-Eventbatch.completedThe event name, mirroring the event field
X-KYG-Attempt1, 2, 3Which retry this is. 1 is the first attempt

Body

FieldTypeDescription
eventstringAlways batch.completed, including when the batch failed — it names the notification, not the outcome
batch_idstringThe identifier returned at submission
statusstringHow it finished: completed, partial, expired or failed
total_requestedintegerCPFs in the batch, after duplicates were removed
total_foundintegerCPFs found in the Paag base — this is what you are billed for
total_not_foundintegerCPFs absent from the base
completed_atstring (ISO 8601, UTC)When the batch closed

Read status, not event, to decide what happened. event is the same string on every delivery. A batch that resolved nothing arrives as "event": "batch.completed" with "status": "failed".

Final statuses

statusMeaningWorth retrying?
completedEvery document resolvedNo
partialResolved, but an enrichment source did not answer for some documentsOnly the affected CPFs, sparingly
expiredThe processing window closed with documents still pendingYes — resubmit the pending ones
failedNothing was resolvedYes

expired and partial are deliberately different answers. expired means time ran out, so resubmitting helps. partial means a source did not answer for that CPF — either it has nothing on file, or it was briefly unavailable — so a retry may produce the same result.

What to do next

Verify the signature, acknowledge with 2xx, then read the results:

curl "https://kyg-api.paag.io/api/v1/batches/lote_3c6e0b8a9c15?limit=500" \
  -H "Authorization: Bearer YOUR_API_KEY"

The result is paginated — keep following next_cursor until it comes back null. A thousand results exceed what a single read returns.