Primeiros Passos
Este guia conduz você por uma verificação de identidade completa no sandbox da Inyo. Ao final, você terá criado uma sessão, verificado um documento e uma selfie por meio do widget hospedado e recebido um resultado assinado no seu próprio endpoint.
Pré-requisitos
- Credenciais de sandbox emitidas pela Inyo durante o onboarding:
client_ideclient_secretpara o grant client-credentials- Sua URL base de sandbox e a confirmação de que suas faixas de IP de saída estão na lista de permissões
- Um
webhookSecret— usado para verificar a assinatura dos resultados entregues
- Um endpoint de webhook, acessível publicamente via HTTPS, registrado na Inyo como sua
webhookUrl. Durante o desenvolvimento, um túnel (ngrok, Cloudflare Tunnel) funciona; alternativamente, use a entrega por redirecionamento e dispense o webhook por completo. - Um celular com câmera para o widget. O acesso à câmera exige um contexto seguro, então abra o widget via HTTPS — navegadores desktop também funcionam.
- Um cliente REST (cURL, Postman ou similar).
Ainda não tem credenciais? Entre em contato com nossa equipe de vendas para solicitar acesso ao sandbox.
Etapa 1: Obtenha um Token de Acesso
curl --request POST \
--url https://{FQDN}/oauth/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data client_id=$KYC_CLIENT_ID \
--data client_secret=$KYC_CLIENT_SECRET
{
"access_token": "eyJhbGciOiJSUzI1NiIs…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "sessions verifications"
}
Armazene o access_token e reutilize-o até expirar — consulte Autenticação para o tratamento de renovação.
Etapa 2: Crie uma Sessão de Verificação
userRef é o seu próprio identificador para a pessoa que está sendo verificada. Ele é devolvido em todos os resultados, então use o identificador pelo qual seu sistema já referencia os usuários.
curl --request POST \
--url https://{FQDN}/v1/sessions \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"userRef": "user-123",
"language": "en",
"delivery": { "mode": "webhook" }
}'
{
"sessionId": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
"status": "pending",
"widgetUrl": "https://{FQDN}/verify/8Kd2mQ…"
}
Persista o sessionId associado ao seu usuário agora — é assim que você correlacionará o resultado recebido.
delivery.modetemwebhookcomo padrão. Se você não tiver umawebhookUrlconfigurada, esta chamada retorna422; use{"mode": "redirect", "redirectUrl": "https://you.example/kyc-done"}no lugar.
Etapa 3: Envie o Cliente para o Widget
Abra a widgetUrl no navegador do cliente ou em uma webview nativa, ou entregue-a por SMS ou e-mail. O link contém um código de uso único e é válido por 48 horas.
O cliente seleciona um tipo de documento, fotografa o documento, tira uma selfie e vê o resultado. Você não escreve nenhum código de câmera — consulte Entrega do Widget para detalhes de webview e personalização de marca.
Etapa 4: Receba o Resultado Assinado
Quando a verificação termina, a Inyo faz um POST do resultado para sua webhookUrl com um cabeçalho X-Inyo-Signature:
POST /kyc-result HTTP/1.1 Content-Type: application/json X-Inyo-Signature: t=1704829200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
{
"sessionId": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
"userRef": "user-123",
"status": "approved",
"autoStatus": "approved",
"document": {
"type": "passport",
"number": "A12345678",
"issuingState": "USA",
"issuingCountry": "USA",
"expirationDate": "2033-09-30"
},
"person": {
"firstName": "ALEX",
"lastName": "MORGAN",
"dateOfBirth": "1988-03-04",
"nationality": "USA"
},
"checks": [
{ "name": "document_readable", "status": "passed", "group": "document", "detail": "all core fields extracted" },
{ "name": "document_mrz_valid", "status": "passed", "group": "document", "detail": "all check digits valid" },
{ "name": "document_not_expired", "status": "passed", "group": "document", "detail": "expirationDate 2033-09-30" },
{ "name": "face_match", "status": "passed", "group": "selfie", "detail": "CompareFaces similarity vs threshold 90.0" }
],
"prefillMismatches": [],
"provider": "inyo"
}
Verifique a assinatura antes de confiar no payload. O cabeçalho é uma lista separada por vírgulas: t é o timestamp Unix em que a assinatura foi calculada e v1 é um HMAC-SHA256 com sua webhookSecret como chave sobre os bytes "<t>." + <corpo bruto>.
import crypto from "node:crypto";
const parts = new Map(
(req.headers["x-inyo-signature"] ?? "")
.split(",")
.map((entry) => entry.trim().split("=")),
);
const timestamp = parts.get("t");
const signature = parts.get("v1");
const expected = crypto
.createHmac("sha256", process.env.KYC_WEBHOOK_SECRET)
.update(`${timestamp}.`)
.update(rawBody) // bytes brutos, antes do JSON.parse
.digest("hex");
const valid =
timestamp &&
signature &&
Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300 &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
Duas coisas que este exemplo encurtado omite e que o código de produção precisa: rejeitar um cabeçalho que repita uma versão e comparar os comprimentos antes de timingSafeEqual (ele lança exceção quando diferem). Fazer o parse do corpo e serializá-lo novamente altera espaços em branco e a ordem das chaves e vai quebrar a assinatura. Detalhes completos, incluindo o modo de redirecionamento e a rotação, estão em Recebendo Resultados.
Responda com 2xx para confirmar o recebimento. A Inyo tenta novamente até três vezes com backoff e então para.
Etapa 5: Confirme no Lado do Servidor
GET /v1/sessions/{sessionId} é o registro autoritativo. Use-o para reconciliar, para recuperar um webhook perdido ou sempre que precisar de certeza em vez de um push:
curl --request GET \
--url https://{FQDN}/v1/sessions/$SESSION_ID \
--header "Authorization: Bearer $ACCESS_TOKEN"
{
"sessionId": "9f1c8e42-7c3e-4f2b-9d7a-2b1e5c8f4a10",
"userRef": "user-123",
"status": "approved",
"step": "done",
"deliveryMode": "webhook",
"result": { "…": "the same normalized result" },
"createdAt": "2026-07-31T14:02:11.481Z",
"updatedAt": "2026-07-31T14:04:57.902Z"
}
Trate Estes Três Casos Antes de Entrar em Produção
| Caso | Por que importa | Onde ler |
|---|---|---|
status é in_review | Não é uma resposta final — um humano decide e você é notificado depois. Pode chegar por webhook e em um redirecionamento. | Revisão Manual |
| Dois resultados para uma sessão | Uma decisão de analista ou uma reversão de controle de qualidade reenvia o resultado. Aplique o maior notifiedAt, não a chegada mais recente. | Recebendo Resultados |
| Uma verificação falhou, mas você quer o detalhe | status é o resultado; os limiares por tenant são citados no detail de cada verificação. | Verificações e Decisões |
Próximos Passos
| Página | O que aborda |
|---|---|
| Sessões de Verificação | Pré-preenchimento, travamento de tipo de documento, verificações de dados, limites de captura |
| Entrega do Widget | Incorporação em webview, personalização de marca, localização |
| Verificação Servidor-a-Servidor | Uso da sua própria interface de captura em vez do widget |
| Sandbox e Dados de Teste | Simulando resultados approved, declined e in_review |
