API de verificação ao vivo
Agende uma sessão de verificação de identidade ao vivo para o seu cliente a partir do seu próprio sistema, acompanhe, cancele ou reagende a sessão e receba a decisão do anfitrião por webhook — tudo identificado pelo seu próprio código de referência.
Uma verificação de identidade ao vivo é uma sessão de vídeo ao vivo conduzida por um anfitrião humano: um membro da sua equipe (o anfitrião) atende o solicitante por câmera, confere o documento dele ao vivo e registra uma decisão. Até agora, essas sessões só podiam ser agendadas pelo painel. Os endpoints abaixo permitem que o seu sistema agende uma sessão para um dos seus clientes, acompanhe a sessão e receba o resultado — por API e por webhook — identificado pelo seu próprio código de referência (externalRef).
Todos os endpoints ficam em /api/v1/kyc/conferences e são autenticados com uma chave de API (Authorization: Bearer ck_live_…). Consulte Autenticação.
A verificação Live ID (esta página — uma sessão de vídeo ao vivo com um anfitrião humano) é um recurso diferente da Live ID Validation (logon biométrico para um usuário que retorna, veja Live ID Validation).
Como funciona
sequenceDiagram
participant You as Your system
participant C as Certivant
participant A as Applicant
participant H as Host (your team)
You->>C: POST /conferences (externalRef, host, scheduledFor)
C-->>You: 201 conference + applicantJoinUrl
C->>A: Invitation email / SMS (join link)
C-->>You: webhook conference.scheduled
H->>C: Joins from the dashboard
C-->>You: webhook conference.started
A->>C: Consents + joins the session
H->>C: Records the decision
C-->>You: webhook conference.completed (decision, externalRef)
- Você agenda a sessão para uma solicitação de verificação existente ou cria o solicitante em linha. Você indica o anfitrião — um membro da sua organização que conduz a sessão pelo painel.
- A Certivant convida o solicitante (e-mail e/ou SMS, no idioma dele) e também devolve o link de acesso para você.
- Antes de entrar na sessão, o solicitante responde à tela de consentimento. O consentimento para gravação em vídeo é sempre coletado; o restante da tela pode ser ignorado com
consentScreen: falsequando você já tiver coletado esses consentimentos no seu próprio app. - Quando o anfitrião registra a decisão, você recebe
conference.completedcom a decisão, o efeito dela na verificação e o seuexternalRef.
Antes de começar
| Requisito | Onde |
|---|---|
Chave de API com kyc:write (agendar / alterar) e kyc:read (consultar) | KYC → Desenvolvedor |
| Seu plano inclui videoconferência | Sua assinatura. Caso contrário, 402 |
| Verificação de identidade ao vivo habilitada nas suas configurações | KYC → Configurações → Verificação de identidade ao vivo. Caso contrário, 403 |
| Sua conta está verificada pela Certivant | Processo de onboarding. Caso contrário, 403 |
| O anfitrião tem permissão de aprovação manual | O proprietário, ou uma função / usuário listado nas suas configurações de aprovação manual. Caso contrário, 403 |
| Uma URL de webhook (opcional, para os resultados) | KYC → Configurações → Webhooks — veja Webhooks → Configuração |
Todas as datas e horas são ISO-8601 com fuso horário — 2026-10-06T14:00:00Z ou 2026-10-06T10:00:00-04:00. Uma data e hora sem fuso horário é rejeitada, para que uma sessão nunca caia no horário errado por causa do fuso local de um servidor. As respostas são sempre em UTC (…Z).
Agendar uma verificação ao vivo
POST /api/v1/kyc/conferences Escopo: kyc:write
Corpo da requisição
{
"kycRequestId": "req_01J…",
"host": { "email": "compliance@yourcompany.com" },
"externalRef": "order-10042",
"title": "Account opening — video check",
"scheduledFor": "2026-10-06T10:00:00-04:00",
"language": "fr",
"consentScreen": true,
"applicantFileUploadEnabled": false,
"channels": { "email": true, "sms": true }
}
| Campo | Tipo | Obrigatório | Observações |
|---|---|---|---|
kycRequestId | string | um dos dois | Uma solicitação de verificação existente na sua organização. |
applicant | object | um dos dois | Crie o solicitante + a verificação em linha (veja abaixo). Um solicitante com o mesmo e-mail na sua organização é reaproveitado. |
host.userId / host.email | string | sim (um deles) | O membro que conduz a sessão. Deve ter permissão de aprovação manual. |
externalRef | string | não | Seu código de referência (ID do pedido / caso). Repetido em todas as respostas e em todos os webhooks. Máximo de 200. |
title | string | não | Exibido para o anfitrião. Máximo de 200. |
scheduledFor | data e hora ISO | um dos dois | Quando a sessão acontece. Deve estar no futuro. |
startNow | boolean | um dos dois | true = agendar para agora (o solicitante é convidado imediatamente). |
expiresAt | data e hora ISO | não | Fim da janela de acesso. Padrão: scheduledFor + 24 h. |
language | enum | não | en, fr, es, de, it, pt, nl, pl, ro, tr, ar, zh, ja, ko, hi, vi, id, ru, uk, sv. Idioma do convite, salvo como o idioma preferido do solicitante (qualquer texto sem tradução nesse idioma é enviado em inglês). Padrão: o idioma salvo do solicitante; se não houver, o idioma padrão da sua organização. |
consentScreen | boolean | não | Padrão true — o solicitante confirma o compartilhamento de informações pessoais, a captura de foto e a gravação em vídeo. false = a tela pede somente o consentimento para gravação em vídeo, que nunca pode ser desativado. |
faceCheckSkippable | boolean | não | Omitido: a configuração do formulário da solicitação, senão a da sua organização (a verificação facial é obrigatória, a menos que você a altere). false força a verificação facial como obrigatória na janela de consentimento. true oferece a alternativa não biométrica, somente onde a lei a exigir (p. ex. Quebec): o solicitante pode recusar a verificação facial, nenhum selfie nem prova de vida é executado, e o anfitrião compara visualmente o rosto com o documento de identidade durante a chamada. A janela não inclui a verificação facial quando consentScreen é false. |
applicantFileUploadEnabled | boolean | não | Padrão false — durante a sessão, o solicitante só pode tirar fotos com a câmera. true também permite enviar arquivos do dispositivo. O anfitrião pode alterar isso durante a sessão. |
channels.email / channels.sms | boolean | não | Como o convite é entregue. Padrão: e-mail; SMS para o telefone de um solicitante recém-criado. Para um solicitante existente, o telefone cadastrado recebe SMS somente quando sms: true. Ambos false = nada é enviado — entregue você mesmo o applicantJoinUrl. |
Solicitante em linha (em vez de kycRequestId):
{
"applicant": {
"fullName": "Jane Doe",
"email": "jane@example.com",
"phone": "+14155551234",
"emailVerified": false,
"phoneVerified": false,
"externalRef": "customer-881"
},
"host": { "userId": "usr_01H…" },
"externalRef": "order-10042",
"startNow": true
}
Convites por SMS são cobrados como os convites do painel (primeiro o SMS incluído no seu plano, depois o saldo de créditos). Se nenhum dos dois cobrir, o SMS é ignorado e o e-mail ainda é enviado.
Exemplo
curl -X POST https://app.certivant.com/api/v1/kyc/conferences \
-H "Authorization: Bearer $CERTIVANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"kycRequestId": "req_01J...",
"host": { "email": "compliance@yourcompany.com" },
"externalRef": "order-10042",
"scheduledFor": "2026-10-06T10:00:00-04:00",
"language": "en"
}'
Resposta 201
O objeto da sessão, além de:
{
"id": "cmf…",
"kycRequestId": "req_01J…",
"externalRef": "order-10042",
"status": "scheduled",
"scheduledFor": "2026-10-06T14:00:00.000Z",
"expiresAt": "2026-10-07T14:00:00.000Z",
"applicantJoinUrl": "https://app.certivant.com/forms/conference/3f9c…",
"invitation": { "email": true, "sms": false }
}
applicantJoinUrl é retornado somente aqui e em reenviar convite — a Certivant armazena apenas um hash do link, então ele não pode ser lido depois. applicantWasReused é adicionado para um solicitante em linha. invitation mostra os canais pelos quais o convite foi enviado.
Erros
| Status | Quando |
|---|---|
400 | Validação falhou (error.issues), ambos ou nenhum entre kycRequestId e applicant, ambos ou nenhum entre scheduledFor e startNow, horário no passado, expiresAt não posterior a scheduledFor, ou o anfitrião não é membro da sua organização. |
402 | Seu plano não inclui verificação ao vivo. |
403 | A chave não tem kyc:write; conta pendente de verificação; verificação ao vivo desabilitada nas configurações; ou o anfitrião não tem permissão de aprovação manual. |
404 | kycRequestId não existe na sua organização. |
409 | A verificação já tem uma sessão ativa que ainda pode ser acessada. O corpo traz o conferenceId dela — reagende ou cancele essa sessão em vez disso. Uma nova tentativa de POST, portanto, nunca convida o cliente duas vezes. |
Obter uma verificação ao vivo
GET /api/v1/kyc/conferences/:id Escopo: kyc:read
Retorna o objeto da sessão. Um ID que pertença a outra organização retorna 404.
curl https://app.certivant.com/api/v1/kyc/conferences/cmf… \
-H "Authorization: Bearer $CERTIVANT_API_KEY"
Consultar pelo seu código de referência
GET /api/v1/kyc/conferences/by-external-ref/:externalRef Escopo: kyc:read
A sessão mais recente que contém esse externalRef (codifique-o para URL). Retorna 404 quando não existe nenhuma.
curl https://app.certivant.com/api/v1/kyc/conferences/by-external-ref/order-10042 \
-H "Authorization: Bearer $CERTIVANT_API_KEY"
Listar verificações ao vivo
GET /api/v1/kyc/conferences Escopo: kyc:read
| Consulta | Observações |
|---|---|
status | scheduled, in_progress, completed, cancelled ou expired (uma sessão ainda agendada cuja janela de acesso foi encerrada sem que a sessão acontecesse — um não comparecimento). scheduled exclui as expiradas. |
externalRef | Correspondência exata com o seu código de referência. |
kycRequestId | Sessões de uma verificação. |
scheduledFrom / scheduledTo | Datas e horas ISO, inclusivas, em scheduledFor. |
createdFrom / createdTo | Datas e horas ISO, inclusivas, na data de criação. |
page / limit | Padrão 1 / 20, limit máximo 100. |
Das mais recentes para as mais antigas, com um desempate estável para que a paginação nunca pule ou repita uma linha.
curl "https://app.certivant.com/api/v1/kyc/conferences?status=completed&createdFrom=2026-10-01T00:00:00Z" \
-H "Authorization: Bearer $CERTIVANT_API_KEY"
{
"data": [ { "id": "cmf…", "status": "completed", "externalRef": "order-10042" } ],
"pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
}
Reagendar
POST /api/v1/kyc/conferences/:id/reschedule Escopo: kyc:write
{
"scheduledFor": "2026-10-08T15:30:00+02:00",
"expiresAt": "2026-10-09T15:30:00+02:00",
"title": "Rescheduled video check",
"externalRef": "order-10042"
}
Somente uma sessão scheduled pode ser movida (409 depois que ela começou, foi concluída ou cancelada). O novo horário deve estar no futuro e antes do fim da janela de acesso — passe um expiresAt posterior para estender a janela. title e externalRef são opcionais: omita para manter, ou envie null para limpar. Retorna o objeto da sessão mais previousScheduledFor e dispara conference.rescheduled.
Reagendar não notifica o solicitante — chame reenviar convite para enviar a ele o novo horário.
Cancelar
POST /api/v1/kyc/conferences/:id/cancel Escopo: kyc:write
{ "reason": "Customer cancelled the order" }
O corpo é opcional. Cancela uma sessão scheduled: o link do solicitante deixa de funcionar e conference.cancelled é disparado com o seu reason. Uma sessão que já esteja in_progress retorna 409 — o anfitrião a encerra pela própria sessão. A solicitação de verificação em si permanece inalterada.
Reenviar o convite
POST /api/v1/kyc/conferences/:id/resend-invitation Escopo: kyc:write
{ "channels": { "email": true, "sms": false }, "language": "es" }
Gera um novo link de acesso (o anterior deixa de funcionar) e reenvia o convite pelos channels — padrão e-mail, mais SMS quando há telefone cadastrado. Com ambos os canais false, nada é enviado: use você mesmo o applicantJoinUrl retornado (copyOnly: true). Permitido enquanto a sessão estiver scheduled ou in_progress e a janela de acesso estiver aberta; caso contrário, 409.
O objeto da sessão
{
"id": "cmf…",
"kycRequestId": "req_01J…",
"applicantId": "cma…",
"organizationId": "org_01H…",
"externalRef": "order-10042",
"title": "Account opening — video check",
"status": "completed",
"expired": false,
"scheduledFor": "2026-10-06T14:00:00.000Z",
"expiresAt": "2026-10-07T14:00:00.000Z",
"startedAt": "2026-10-06T14:01:12.000Z",
"endedAt": "2026-10-06T14:14:40.000Z",
"durationSeconds": 808,
"createdAt": "2026-10-01T09:00:00.000Z",
"updatedAt": "2026-10-06T14:14:40.000Z",
"host": { "userId": "usr_01H…", "name": "Sam Reviewer", "email": "compliance@yourcompany.com" },
"applicant": {
"id": "cma…",
"fullName": "Jane Doe",
"email": "jane@example.com",
"externalRef": "customer-881",
"joinedAt": "2026-10-06T14:02:03.000Z"
},
"additionalApplicants": [],
"additionalKycRequestIds": [],
"consent": {
"screenEnabled": true,
"requiredPurposes": ["share_information", "photo_capture", "video_recording"],
"given": true,
"at": "2026-10-06T14:01:58.000Z",
"version": "2026-10-04"
},
"decision": {
"decision": "verified",
"outcome": "approved",
"requestStatus": "APPROVED",
"notes": "ID checked on camera, matches the applicant.",
"decidedByUserId": "usr_01H…",
"documentDecisions": { "verified": 2, "rejected": 0, "needsResubmission": 0 }
},
"recording": { "available": true, "deliveryStatus": "delivered", "durationSeconds": 790, "sha256": "9b1f…" },
"certificate": null
}
| Campo | Observações |
|---|---|
status | scheduled → in_progress (o anfitrião entrou) → completed (decisão registrada ou sessão encerrada), ou cancelled. |
expired | true quando uma sessão scheduled teve sua janela de acesso encerrada sem que a sessão acontecesse (não comparecimento). Filtre com status=expired. |
consent.given | A resposta do solicitante principal: null até que ele responda, false se ele recusar (a sessão é então cancelada). requiredPurposes sempre contém video_recording. |
decision.decision | O veredito do anfitrião: verified, rejected, needs_more_info ou inconclusive. null até a decisão. |
decision.outcome | O efeito sobre a verificação: approved, rejected ou escalated (needs_more_info / inconclusive a deixam em revisão). requestStatus é o status da verificação. |
recording.available | true quando a gravação está montada e armazenada ou entregue ao seu armazenamento. A mídia em si nunca é exposta por esta API. |
certificate | O Certificado de Verificação da verificação, quando um foi emitido: id, certificateNumber, verificationUrl. |
Webhooks
As verificações ao vivo emitem cinco eventos para a sua URL de webhook (a URL própria do formulário, quando a verificação veio de um formulário que tenha uma; caso contrário, o padrão da sua organização). Eles são disparados para todas as sessões — agendadas pelo painel ou pela API — assinados e reenviados exatamente como todos os outros webhooks da Certivant.
| Evento | Disparado quando |
|---|---|
conference.scheduled | Uma sessão é criada (agendada para depois, ou iniciada agora). |
conference.rescheduled | Uma sessão agendada muda para um novo horário. Traz previousScheduledFor. |
conference.cancelled | Uma sessão é cancelada — por sua chamada de API ou porque o solicitante recusou o consentimento. Traz cancellation. |
conference.started | O anfitrião inicia a sessão (primeira entrada). Uma vez por início — reconexões do anfitrião não repetem o evento. |
conference.completed | O anfitrião registra a decisão final. Traz decision. |
Um reenvio de convite não é um novo conference.scheduled.
Ainda não existe um evento no_show. Uma sessão que nunca acontece permanece
scheduled e é reportada com expired: true quando a sua janela de acesso
se encerra — consulte GET /conferences?status=expired para encontrar os
não comparecimentos.
Payload
Diferentemente dos eventos kyc.request.*, os eventos de sessão usam um corpo no formato de sessão. Cada evento traz os IDs, o seu externalRef, o status e os carimbos de data e hora:
{
"event": "conference.completed",
"conferenceId": "cmf…",
"kycRequestId": "req_01J…",
"applicantId": "cma…",
"organizationId": "org_01H…",
"externalRef": "order-10042",
"title": "Account opening — video check",
"status": "completed",
"scheduledFor": "2026-10-06T14:00:00.000Z",
"expiresAt": "2026-10-07T14:00:00.000Z",
"startedAt": "2026-10-06T14:01:12.000Z",
"endedAt": null,
"durationSeconds": null,
"createdAt": "2026-10-01T09:00:00.000Z",
"updatedAt": "2026-10-06T14:13:55.000Z",
"host": { "userId": "usr_01H…", "name": "Sam Reviewer", "email": "compliance@yourcompany.com" },
"consent": {
"screenEnabled": true,
"requiredPurposes": ["share_information", "photo_capture", "video_recording"],
"given": true,
"at": "2026-10-06T14:01:58.000Z"
},
"decision": {
"decision": "rejected",
"outcome": "rejected",
"requestStatus": "REJECTED",
"notes": "Face does not match the ID photo.",
"decidedByUserId": "usr_01H…",
"documentDecisions": { "verified": 0, "rejected": 1, "needsResubmission": 0 }
},
"recording": { "available": false, "deliveryStatus": "pending", "durationSeconds": null, "sha256": null },
"certificate": null,
"timestamp": "2026-10-06T14:13:55.000Z"
}
Campos específicos de cada evento:
conference.rescheduled—previousScheduledFor.conference.cancelled—cancellation.reasonecancellation.cancelledBy(api,host,applicant,system). Um consentimento recusado chega comocancelledBy: "applicant",reason: "applicant_refused_consent".conference.completed—decision(como no objeto da sessão). A sessão ainda pode estar em andamento quando a decisão é registrada, entãoendedAtpode sernull, e a gravação costuma ainda estar sendo montada (recording.available: false) — leiaGET /conferences/:iddepois para ver o estado final da gravação.
Os payloads nunca contêm mídia, links de gravação ou tokens de acesso.
Verificar a assinatura
Cada entrega é assinada com o seu segredo de assinatura de webhook. O cabeçalho recomendado é X-Certivant-Signature-V2: t=<unix>,v1=<hex>, em que v1 é o HMAC-SHA256 de `${t}.${rawBody}`. Rejeite timestamps com mais de 300 segundos de diferença e elimine duplicatas com base em X-Certivant-Event-Id. (Os cabeçalhos mais antigos X-KYC-Signature / X-KYC-Timestamp / X-KYC-Event-Id ainda são enviados.)
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyCertivantWebhook(rawBody: string, headers: Headers, secret: string) {
const parts = (headers.get("X-Certivant-Signature-V2") ?? "").split(",").map((p) => p.split("="));
const t = parts.find(([k]) => k === "t")?.[1] ?? "";
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return parts.some(
([k, v]) => k === "v1" && v?.length === expected.length && timingSafeEqual(Buffer.from(v), Buffer.from(expected)),
);
}
// In your handler:
const event = JSON.parse(rawBody);
if (event.event === "conference.completed") {
await orders.markIdentityChecked(event.externalRef, event.decision.outcome);
}
Detalhes completos e em outros idiomas: Verificação de assinatura.
Limites de requisições
Estes endpoints compartilham o limite de requisições da API da sua organização com o restante da API v1 — veja Limites de requisições.
Sessões hospedadas
Gere uma URL de curta duração na qual o solicitante conclui a captura de documentos, a prova de vida e as etapas de identidade.
API de lista de bloqueio
Adicione, liste e remova entradas da lista de bloqueio do seu próprio sistema — endereços de e-mail, números de telefone, números de documentos e endereços IP ou intervalos, ou o rosto, os dispositivos e o IP associados a uma verificação existente.