CPFHub.io

Identity Verification

Create a verification link, send it to the person and get the decision. The verification page, hosted by CPFHub.io, runs the liveness check, reads the document (CNH, RG or passport), compares the face with the document photo and checks the CPF against the Receita Federal. It costs 5 credits per completed verification.

Using Cursor, Lovable, v0 or another AI? Copy the Identity Verification prompt and paste it into your assistant.

Open in Cursor
  1. POST /kyc/links creates the link and returns the url.
  2. You send the url to the person or redirect them to it in your sign-up flow.
  3. The decision arrives at your webhook, or through the GET /kyc/links/{id} lookup.

The verification does not validate the document against a government database, does not perform forensic document analysis and does not accept the digital CNH (CNH Digital).

POSThttps://api.cpfhub.io/kyc/links
WhereNameRequiredDescription
Headerx-api-keyYesYour API key, the same one used for lookups
BodyexternalIdNoYour identifier for the sign-up (up to 200 characters). It comes back in the webhook.
BodycpfNoA CPF you already know. If it differs from the document, the verification goes to review. It is never returned in responses.
BodyredirectUrlNoWhere the person returns when they finish. https only.
BodylanguageNopt-BR, en or es. Default: browser language.
BodyexpiresInMinutesNoValidity, from 5 to 10080. Default: 1440 (24 h).
BodymaxAttemptsNoAttempts, from 1 to 10. Default: 3.
curl -X POST "https://api.cpfhub.io/kyc/links" \
  -H "x-api-key: $CPFHUB_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "externalId": "cliente-4821",
    "redirectUrl": "https://seusite.com.br/cadastro/concluido"
  }'

HTTP 201 Created

JSON
{
  "success": true,
  "data": {
    "id": "5b1d7c2e-8a4f-4e7b-9c3d-2f6a1e0b9d47",
    "url": "https://verificar.cpfhub.io/AbC123xYz",
    "externalId": "cliente-4821",
    "expiresAt": "2026-10-28T14:00:00.000Z",
    "maxAttempts": 3,
    "language": null
  }
}

Store the id: it comes back in the webhook as linkId. When they finish, the person returns to redirectUrl with kyc_link_id and external_id in the URL, but without the decision: confirm it through the webhook or the lookup.

GEThttps://api.cpfhub.io/kyc/links/{id}
JSON
{
  "success": true,
  "data": {
    "id": "5b1d7c2e-8a4f-4e7b-9c3d-2f6a1e0b9d47",
    "url": null,
    "externalId": "cliente-4821",
    "status": "completed",
    "attempts": 1,
    "maxAttempts": 3,
    "expiresAt": "2026-10-28T14:00:00.000Z",
    "createdAt": "2026-10-27T14:00:00.000Z",
    "completedAt": "2026-10-27T14:06:12.000Z",
    "verification": {
      "id": "8f3c2a1e-77b0-4d3c-9a1f-0e5b6c7d8e9f",
      "status": "approved",
      "reasons": [],
      "completedAt": "2026-10-27T14:06:12.000Z"
    }
  }
}

Link status: pending, completed, expired or canceled. url is returned only while the link can be used. verification is null until the person finishes. No personal data.

Decision

verification.statusMeaning
approvedAll good. The link is closed.
manual_reviewNeeds someone on your team (see reasons). Review it in the dashboard. The link is closed.
rejectedRejected by the account rules. The person can try again while attempts remain.

The reasons come in reasons as [{ "code", "message" }]. The most common: face_similarity_borderline, document_expired, ocr_failed, cpf_document_mismatch, cpf_name_mismatch and cpf_situation_irregular. Treat an unknown code as review and show the message only to your team.

Settings

Accepted documents, face similarity thresholds, manual review, expired documents, branding, retention and the webhook are in the dashboard, under Verificações > Configurações, and apply to every link in the account.

Errors

StatusWhen it happensWhat to do
400Invalid parameter (redirectUrl without https, invalid cpf, value out of range)Fix the body. Do not retry.
401API key missing, invalid or inactiveCheck the x-api-key header.
403Inactive or blocked accountContact support.
404Link does not exist or belongs to another API keyCheck the id.
503Temporary failureRetry with increasing waits (2 s, 4 s, 8 s).

Error format: Error Codes.

Cost

5 credits per completed verification, from the same balance as lookups, even when it is rejected. The CPF cross-check is included. Creating and looking up links costs nothing, and a technical failure on our side is not charged.

⚠

The link gives access to the verification

Anyone with the url can complete the verification in the person's place. Create one link per person, send it only to them and use the shortest validity and attempts your flow allows.


Updated on October 27, 2026