Referencia de la APIAPI de verificación en vivo

API de verificación en vivo

Programe una sesión de verificación de identidad en vivo (Live ID) para su cliente desde su propio sistema, haga su seguimiento, cancélela o reprográmela, y reciba la decisión del anfitrión por webhook. Todo se identifica con su propio código de referencia.

Una verificación de identidad en vivo es una sesión de video en directo dirigida por una persona: uno de los miembros de su equipo (el anfitrión) se reúne con el solicitante frente a la cámara, comprueba su identificación en vivo y registra una decisión. Hasta ahora, estas sesiones solo podían programarse desde el panel. Los endpoints siguientes permiten que su sistema programe una sesión para uno de sus clientes, le haga seguimiento y reciba el resultado, tanto por API como mediante webhook, identificada con su propio código de referencia (externalRef).

Todos los endpoints se encuentran bajo /api/v1/kyc/conferences y se autentican con una clave de API (Authorization: Bearer ck_live_…). Consulte Autenticación.

Verificación de identidad en vivo (esta página: una sesión de video en directo con un anfitrión humano) es una función distinta de Live ID Validation (inicio de sesión biométrico para un usuario que vuelve, consulte Live ID Validation).

Cómo 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)
  1. Usted programa la sesión para una solicitud de verificación existente o crea el solicitante en línea. Debe indicar el anfitrión: un miembro de su organización que dirige la sesión desde el panel.
  2. Certivant invita al solicitante (por correo electrónico y/o SMS, en su idioma) y también le devuelve el enlace de acceso.
  3. Antes de entrar en la sesión, el solicitante responde a la ventana de consentimiento. El consentimiento para la grabación de video siempre se recaba; el resto de la ventana puede omitirse con consentScreen: false cuando usted ya haya recabado esos consentimientos en su propia aplicación.
  4. Cuando el anfitrión registra la decisión, usted recibe conference.completed con la decisión, su efecto en la verificación y su externalRef.

Antes de empezar

RequisitoDónde
Clave de API con kyc:write (programar / modificar) y kyc:read (leer)KYC → Desarrollador
Su plan incluye videoconferencia webSu suscripción. De lo contrario, 402
Verificación de identidad en vivo habilitada en su configuraciónKYC → Configuración → Verificación de identidad en vivo. De lo contrario, 403
Su cuenta está verificada por CertivantIncorporación (onboarding). De lo contrario, 403
El anfitrión tiene permiso de aprobación manualEl propietario, o un rol o usuario incluido en su configuración de aprobación manual. De lo contrario, 403
Una URL de webhook (opcional, para los resultados)KYC → Configuración → Webhooks: consulte Webhooks → Configuración

Todas las fechas y horas son ISO-8601 con zona horaria: 2026-10-06T14:00:00Z o 2026-10-06T10:00:00-04:00. Una fecha y hora sin zona horaria se rechaza, de modo que una sesión nunca puede quedar a una hora equivocada por culpa de la zona local de un servidor. Las respuestas siempre están en UTC (…Z).


Programar una verificación en vivo

POST /api/v1/kyc/conferences    Alcance: kyc:write

Cuerpo de la solicitud

{
  "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 }
}
CampoTipoObligatorioNotas
kycRequestIdstringuno deUna solicitud de verificación existente en su organización.
applicantobjectuno deCrea el solicitante y la verificación en línea (véase más abajo). Se reutiliza un solicitante con el mismo correo electrónico en su organización.
host.userId / host.emailstringsí (uno)El miembro que dirige la sesión. Debe tener permiso de aprobación manual.
externalRefstringnoSu código de referencia (ID de pedido o de expediente). Se repite en cada respuesta y en cada webhook. Máximo 200.
titlestringnoSe muestra al anfitrión. Máximo 200.
scheduledForISO date-timeuno deMomento en que tiene lugar la sesión. Debe ser futuro.
startNowbooleanuno detrue = programar para este mismo momento (el solicitante se invita de inmediato).
expiresAtISO date-timenoFin de la ventana de acceso. Predeterminado: scheduledFor + 24 h.
languageenumnoen, fr, es, de, it, pt, nl, pl, ro, tr, ar, zh, ja, ko, hi, vi, id, ru, uk, sv. Idioma de la invitación, guardado como idioma preferido del solicitante (cualquier texto sin traducción a ese idioma se envía en inglés). Predeterminado: el idioma guardado del solicitante; si no hay ninguno, el idioma predeterminado de su organización.
consentScreenbooleannoPredeterminado true: el solicitante confirma que comparte información personal, la captura de fotografía y la grabación de video. false = la ventana pide solo el consentimiento para la grabación de video, que nunca puede desactivarse.
faceCheckSkippablebooleannoOmitido: el ajuste del formulario de la solicitud, si no el de su organización (la verificación facial es obligatoria salvo que lo cambie). false fuerza que la verificación facial sea obligatoria en la ventana de consentimiento. true ofrece la alternativa no biométrica, solo donde la ley la exija (p. ej. Quebec): el solicitante puede rechazar la verificación facial, no se ejecuta ninguna selfie ni prueba de vida, y el anfitrión compara visualmente el rostro con el documento de identidad durante la llamada. La ventana no incluye la verificación facial cuando consentScreen es false.
applicantFileUploadEnabledbooleannoPredeterminado false: durante la sesión, el solicitante solo puede tomar fotos con su cámara. true permite además enviar archivos desde su dispositivo. El anfitrión puede cambiarlo durante la sesión.
channels.email / channels.smsbooleannoCómo se entrega la invitación. Predeterminado: correo electrónico; SMS al teléfono de un solicitante recién creado. En un solicitante existente, su teléfono registrado solo recibe un SMS cuando sms: true. Ambos false = no se envía nada; en ese caso, entregue usted mismo applicantJoinUrl.

Solicitante en línea (en lugar 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
}

Las invitaciones por SMS se facturan igual que las invitaciones del panel (primero los SMS incluidos en su plan y después su saldo de crédito). Si ninguno de los dos lo cubre, el SMS se omite y el correo electrónico sí se envía.

Ejemplo

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"
  }'

Respuesta 201

El objeto de conferencia, más:

{
  "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 se devuelve solo aquí y en reenviar la invitación. Certivant almacena únicamente un hash del enlace, por lo que no puede leerse más adelante. applicantWasReused se añade para un solicitante en línea. invitation indica los canales por los que se envió la invitación.

Errores

EstadoCuándo
400La validación falló (error.issues), faltan o sobran ambos kycRequestId y applicant, faltan o sobran ambos scheduledFor y startNow, la hora está en el pasado, expiresAt no es posterior a scheduledFor, o el anfitrión no es miembro de su organización.
402Su plan no incluye la verificación en vivo.
403La clave no tiene kyc:write; la cuenta está pendiente de verificación; la verificación en vivo está deshabilitada en la configuración; o el anfitrión no tiene permiso de aprobación manual.
404El kycRequestId no existe en su organización.
409La verificación ya tiene una conferencia a la que es posible unirse. El cuerpo incluye su conferenceId: reprográmela o cancélela en su lugar. Así, un POST reintentado nunca invita dos veces al cliente.

Obtener una verificación en vivo

GET /api/v1/kyc/conferences/:id    Alcance: kyc:read

Devuelve el objeto de conferencia. Un ID que pertenece a otra organización responde con 404.

curl https://app.certivant.com/api/v1/kyc/conferences/cmf… \
  -H "Authorization: Bearer $CERTIVANT_API_KEY"

Buscar por su código de referencia

GET /api/v1/kyc/conferences/by-external-ref/:externalRef    Alcance: kyc:read

La conferencia más reciente que tenga ese externalRef (codifíquelo para la URL). Responde 404 si no existe ninguna.

curl https://app.certivant.com/api/v1/kyc/conferences/by-external-ref/order-10042 \
  -H "Authorization: Bearer $CERTIVANT_API_KEY"

Listar verificaciones en vivo

GET /api/v1/kyc/conferences    Alcance: kyc:read

ConsultaNotas
statusscheduled, in_progress, completed, cancelled o expired (una sesión todavía programada cuya ventana de acceso se cerró sin que la sesión tuviera lugar: una ausencia). scheduled excluye las caducadas.
externalRefCoincidencia exacta con su código de referencia.
kycRequestIdConferencias de una misma verificación.
scheduledFrom / scheduledToFechas y horas ISO, inclusivas, sobre scheduledFor.
createdFrom / createdToFechas y horas ISO, inclusivas, sobre la fecha de creación.
page / limitPredeterminado 1 / 20; limit máximo 100.

Las más recientes primero, con un criterio de desempate estable para que la paginación nunca omita ni repita una fila.

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 }
}

Reprogramar

POST /api/v1/kyc/conferences/:id/reschedule    Alcance: 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"
}

Solo se puede mover una conferencia scheduled (409 una vez que ha empezado, se ha completado o se ha cancelado). La nueva hora debe ser futura y anterior al final de la ventana de acceso: pase un expiresAt posterior para ampliar la ventana. title y externalRef son opcionales: omítalos para conservarlos o envíe null para borrarlos. Devuelve el objeto de conferencia más previousScheduledFor, y dispara conference.rescheduled.

Reprogramar no notifica al solicitante. Llame a reenviar la invitación para enviarle la nueva hora.

Cancelar

POST /api/v1/kyc/conferences/:id/cancel    Alcance: kyc:write

{ "reason": "Customer cancelled the order" }

El cuerpo es opcional. Cancela una conferencia scheduled: el enlace del solicitante deja de funcionar y se dispara conference.cancelled con su reason. Una sesión que ya está in_progress responde con 409: el anfitrión la finaliza desde la sesión. La solicitud de verificación en sí no cambia.

Reenviar la invitación

POST /api/v1/kyc/conferences/:id/resend-invitation    Alcance: kyc:write

{ "channels": { "email": true, "sms": false }, "language": "es" }

Genera un nuevo enlace de acceso (el anterior deja de funcionar) y vuelve a enviar la invitación por los channels indicados: por defecto, correo electrónico, además de SMS cuando hay un teléfono registrado. Con ambos canales en false no se envía nada: use usted mismo el applicantJoinUrl devuelto (copyOnly: true). Se permite mientras la conferencia esté scheduled o in_progress y su ventana de acceso siga abierta; en caso contrario, 409.


El objeto de conferencia

{
  "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
}
CampoNotas
statusscheduled → in_progress (el anfitrión se ha unido) → completed (decisión registrada o sesión finalizada), o cancelled.
expiredtrue cuando una sesión scheduled cerró su ventana de acceso sin que la sesión tuviera lugar (ausencia). Filtre con status=expired.
consent.givenLa respuesta del solicitante principal: null hasta que responde, false si lo rechazó (en ese caso la sesión se cancela). requiredPurposes siempre contiene video_recording.
decision.decisionEl veredicto del anfitrión: verified, rejected, needs_more_info o inconclusive. null hasta que se decide.
decision.outcomeLo que hizo con la verificación: approved, rejected o escalated (needs_more_info e inconclusive la dejan en espera de revisión). requestStatus es el estado de la verificación.
recording.availabletrue cuando la grabación está ensamblada y almacenada o entregada a su almacenamiento. El contenido multimedia en sí nunca se expone por esta API.
certificateEl Certificado de verificación de la verificación, cuando se emitió: id, certificateNumber, verificationUrl.

Webhooks

Las verificaciones en vivo emiten cinco eventos a su URL de webhook (la URL específica del formulario cuando la verificación procede de un formulario que la tenga; si no, la URL predeterminada de su organización). Se disparan para cada conferencia, esté programada desde el panel o desde la API, y se firman y reintentan exactamente igual que todos los demás webhooks de Certivant.

EventoSe dispara cuando
conference.scheduledSe crea una conferencia (programada para más adelante, o de inicio inmediato).
conference.rescheduledUna conferencia programada cambia a una nueva hora. Incluye previousScheduledFor.
conference.cancelledSe cancela una conferencia, ya sea por su llamada a la API o porque el solicitante rechazó el consentimiento. Incluye cancellation.
conference.startedEl anfitrión inicia la sesión (primera conexión). Se envía una vez por inicio; las reconexiones del anfitrión no lo repiten.
conference.completedEl anfitrión registra la decisión final. Incluye decision.

Reenviar la invitación no genera un nuevo conference.scheduled.

Todavía no existe un evento no_show. Una sesión que nunca tiene lugar permanece en scheduled y se informa con expired: true cuando se cierra su ventana de acceso. Consulte GET /conferences?status=expired para encontrar las ausencias.

Carga útil

A diferencia de los eventos kyc.request.*, los eventos de conferencia usan un cuerpo con la forma de una conferencia. Cada evento incluye los identificadores, su externalRef, el estado y las marcas de tiempo:

{
  "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.reason y cancellation.cancelledBy (api, host, applicant, system). Un consentimiento rechazado llega como cancelledBy: "applicant", reason: "applicant_refused_consent".
  • conference.completed — decision (como en el objeto de conferencia). La sesión puede seguir en curso cuando se registra la decisión, por lo que endedAt puede ser null, y la grabación suele seguir ensamblándose (recording.available: false). Lea GET /conferences/:id más tarde para conocer el estado final de la grabación.

Las cargas útiles nunca contienen contenido multimedia, enlaces de grabación ni tokens de acceso.

Verificar la firma

Cada entrega se firma con su secreto de firma de webhook. El encabezado recomendado es X-Certivant-Signature-V2: t=<unix>,v1=<hex>, donde v1 es el HMAC-SHA256 de `${t}.${rawBody}`. Rechace las marcas de tiempo que difieran más de 300 segundos y elimine los duplicados según X-Certivant-Event-Id. (Los encabezados anteriores X-KYC-Signature / X-KYC-Timestamp / X-KYC-Event-Id todavía se envían).

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);
}

Detalles completos y otros idiomas: Verificación de firma.


Límites de tasa

Estos endpoints comparten el límite de tasa de la API de su organización con el resto de la API v1. Consulte Límites de tasa.