KYC Magic Link
Este caso de uso no utiliza el endpoint de Unico POST /v1/process ni el contrato API/TCA. La integración es con Trully.ai (host api.trully.ai), con autenticación mediante x-api-key en lugar de Bearer JWT, y un esquema de respuesta propietario. Para otros países, use Onboarding (Global).
Qué resuelve este caso de uso
KYC Magic Link aborda el desafío de ejecutar el proceso de identificación en México, recopilando documentos de identidad nacionales (INE) y biometría facial. Con un journey alojado por Unico, se elimina la fricción de desarrollo front-end mediante un enlace enviado a través de sus propios canales (WhatsApp, SMS, correo electrónico).
Use este caso de uso cuando:
- Opera en México y el documento de identidad utilizado es el INE (obligatorio).
No use este caso de uso cuando:
- El usuario está fuera de México o utiliza otros documentos → consulte los otros casos de uso de onboarding.
Capacidades involucradas
Pipeline ejecutado dentro de un único proceso:
| Capacidad | Obligatoria | Rol en el flujo |
|---|---|---|
| Captura de Documento | Obligatoria | Captura la imagen del documento INE. La reutilización de documentos no está disponible en este caso de uso — cada sesión requiere una nueva captura. |
| Liveness | Obligatoria | Verificación de vida — selfie obligatoria que ancla el proceso. |
| Clasificación de Riesgo | Obligatoria | Cruza señales de comportamiento para marcar el riesgo de fraude asociado al CPF. |
| Verificación de Identidad | Opcional (si contratada) | Verifica si el rostro de la transacción pertenece al titular del identificador gubernamental proporcionado, usando la base de identidad de Unico y señales adicionales. |
Prerequisitos
- API key — aprovisionada por su Project Manager de Onboarding en Unico. Se envía en el encabezado
x-api-key. - Endpoint HTTPS público para recibir webhooks (opcional, pero recomendado).
- Configuración CORS — permita los orígenes
https://verification.unico.app(producción) yhttps://verification.uat.unico.app(sandbox) en el servidor que recibe el webhook.
Implementación paso a paso
A diferencia de otros casos de uso, Magic Link no tiene campo flow — la integración es directa con la API de Trully. El endpoint crea un enlace de verificación único que usted distribuye al usuario a través de su propio canal.
Endpoint: POST https://sandbox.trully.ai/v2/magic-link
Encabezados:
| Encabezado | Obligatorio | Descripción |
|---|---|---|
x-api-key | Sí | API key aprovisionada por su Project Manager de Onboarding en Unico. |
Content-Type | Sí | application/json |
Cuerpo (application/json):
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
external_id | string | No | Un identificador externo para la solicitud, utilizado para seguimiento y referencia. |
metadata | object | No | Contenedor para los parámetros de configuración. |
metadata.phone | string | No | Número de teléfono del usuario en formato internacional (p. ej., 521234567890). |
metadata.redirect_url | string | No | URL a la que redirigir al usuario una vez completado el proceso KYC. |
metadata.webhook_url | string | No | URL a la que enviar los datos del proceso KYC una vez completado. Este webhook se invocará del lado del cliente. Por razones de seguridad, el endpoint debe usar HTTPS. El componente esperará un minuto a que su servidor webhook responda — después de eso, interrumpirá la comunicación. El proceso no se verá afectado de ninguna manera por la comunicación del webhook. Asegúrese de permitir https://verification.unico.app y https://verification.uat.unico.app en su configuración CORS para producción y sandbox respectivamente. |
metadata.track_webhook_url | string | No | URL a la que enviar cada paso KYC procesado por el usuario (consulte los eventos de webhook a continuación). Este webhook se invocará del lado del cliente. Por razones de seguridad, el endpoint debe usar HTTPS. El componente esperará un minuto a que su servidor webhook responda — después de eso, interrumpirá la comunicación. El proceso no se verá afectado de ninguna manera por la comunicación del webhook. Asegúrese de permitir https://verification.unico.app y https://verification.uat.unico.app en su configuración CORS para producción y sandbox respectivamente. |
Valores posibles para data.step (enviados a track_webhook_url):
| Paso | Descripción |
|---|---|
form_start | El usuario está escaneando el documento. |
form_document_front | El usuario ya capturó el frente del INE. |
form_document_back | El usuario ya capturó el reverso del INE. |
form_document | El proceso está actualmente en el paso de selfie. |
form_selfie | El usuario llegó al paso final de la operación. |
form_decision_maker | La operación devuelve la respuesta del sistema. |
Ejemplo de solicitud:
curl -X POST https://sandbox.trully.ai/v2/magic-link \
-H "x-api-key: $TRULLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"external_id": "user-mx-12345",
"metadata": {
"phone": "521234567890",
"redirect_url": "https://app.client.com.mx/kyc-done",
"webhook_url": "https://app.client.com.mx/webhook/result",
"track_webhook_url": "https://app.client.com.mx/webhook/track"
}
}'
Campos de la respuesta:
| Campo | Tipo | Descripción |
|---|---|---|
data.external_id | string (nullable) | Un identificador externo para la solicitud, utilizado para seguimiento y referencia. |
data.created_on | string (date-time) | La fecha en que se creó el magic link. |
data.is_active | boolean | Si es true, el magic link está activo. |
data.token | string | El token del magic link que se usará para vincular los procesos KYC al magic link. |
data.magic_link_url | string (URI) | La URL del magic link para iniciar el proceso KYC. |
data.metadata.redirect_url | string (nullable) | URL a la que redirigir al usuario una vez completado el proceso KYC. |
data.metadata.webhook_url | string (nullable) | URL a la que enviar los datos del proceso KYC una vez completado. Este webhook se invocará del lado del cliente. |
data.metadata.track_webhook_url | string (nullable) | URL a la que enviar cada paso KYC procesado por el usuario. Este webhook se invocará del lado del cliente. |
data.version | string | La versión del magic link. |
version | string | La versión de la API que procesó la solicitud, útil para rastrear cambios y compatibilidad. |
status | string | Representación textual del estado de la respuesta, indicando éxito o fallo de la operación. |
status_code | integer | El código de estado HTTP de la respuesta, proporcionando un indicador estandarizado del resultado de la solicitud. |
request_date | string (date-time) | La fecha y hora en que se realizó la solicitud, en formato ISO 8601. |
request.metadata.redirect_url | string (nullable) | URL a la que redirigir al usuario una vez completado el proceso KYC (eco de la solicitud). |
request.metadata.webhook_url | string (nullable) | URL a la que enviar los datos del proceso KYC una vez completado (eco de la solicitud). |
request.metadata.track_webhook_url | string (nullable) | URL a la que enviar cada paso KYC procesado por el usuario (eco de la solicitud). |
Ejemplo de respuesta:
{
"data": {
"external_id": null,
"created_on": "2025-07-28T18:11:54.430048399Z",
"is_active": true,
"token": "3f6dbcc1-49ba-4935-be90-dd8dd59b5530",
"magic_link_url": "https://verification.uat.unico.app/link/v2/3f6dbcc1-49ba-4935-be90-dd8dd59b5530",
"metadata": {
"redirect_url": null,
"webhook_url": null,
"track_webhook_url": null
},
"version": "v2"
},
"version": "v1.4.2",
"status": "ok",
"status_code": 200,
"request_date": "2025-07-28T18:11:54+0000",
"request": {
"metadata": {
"redirect_url": null,
"webhook_url": null,
"track_webhook_url": null
}
}
}
Respuestas de error — POST /v2/magic-link
| Código | Mensaje | Descripción |
|---|---|---|
400 Bad Request | data provided in the field is invalid | Estructura de datos o valores de campo inválidos en el payload de la solicitud. Verifique los campos obligatorios y sus formatos. |
403 Forbidden | Forbidden | API key ausente, expirada o sin permiso. Verifique el encabezado x-api-key. |
500 Internal Server Error | internal server error | Fallo de procesamiento en el servidor. Reintente con backoff exponencial. Si persiste, repórtelo al soporte. |
Ejemplo de respuesta 400:
{
"data": {
"error": "data provided in the field is invalid"
},
"version": "v1.4.2",
"status": "bad request",
"status_code": 400,
"request_date": "2025-07-28T20:22:29+0000",
"request": {
"metadata": null
}
}
Envíe por WhatsApp, SMS, correo electrónico o incrústelo. El usuario accede al enlace desde su propio dispositivo y completa el journey alojado.
- Polling mediante GET (obligatorio) — llame a
GET /v2/history/request?magic_link_token={token}periódicamente hasta queunico.resultesté poblado. Consulte Polling mediante GET a continuación. - Webhook (opcional) — configúrelo con su PM de Onboarding para recibir eventos automáticamente. Consulte Webhook a continuación.
Polling mediante GET
Endpoint: GET https://sandbox.trully.ai/v2/history/request
Parámetros de consulta:
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
magic_link_token | string | Sí | Token devuelto al crear el Magic Link. |
Encabezados:
| Encabezado | Obligatorio | Descripción |
|---|---|---|
x-api-key | Sí | API key aprovisionada por su Project Manager de Onboarding en Unico. |
Ejemplo de solicitud:
curl "https://sandbox.trully.ai/v2/history/request?magic_link_token=$TOKEN" \
-H "x-api-key: $TRULLY_API_KEY"
Ejemplo de respuesta:
{
"data": {
"images": {
"document_image": "/9j/4ASu7bmV[...]fyPjOKfgif//Z",
"document_image_back": "/9j/4ASu7bmV[...]fyPjOKfgif//Z",
"selfie": "/9j/4ASu7bmV[...]fyPjOKfgif//Z"
},
"response": {
"curp": {
"age": 58,
"curp": "GOCJ850627HDFRRL09",
"date_of_birth": "14/11/1956",
"deceased": false,
"gender": "M",
"government_name": "LUKE SKYWALKER",
"government_valid": true,
"is_mexican": true,
"name_to_CURP_valid": true,
"state_iso": "MX-NLE",
"state_of_birth": "Nuevo León"
},
"document": {
"back": {
"cic": "237457894",
"citizen_id": "237457894",
"mrz": "IDMEX999999999999<9 VADER<SKYWALKER<<LUKE"
},
"details": {
"detected": true,
"document_id": 229928,
"forensics": { "is_valid": "no" }
},
"front": {
"face_analysis": {
"face_id": 237437,
"face_id_v2": 199068,
"first_seen": "12/22/2022, 18:54:09",
"inquiry_date": "07/28/2025, 20:53:12",
"last_seen": "07/28/2025, 18:47:46",
"last_seen_by_your_company": "07/24/2025, 21:38:21",
"match": true,
"match_fraud_flag": true,
"seen_by_your_company": true,
"seen_different_companies": 46,
"times_seen_by_your_company": 3,
"times_seen_last_month": 111,
"unique_face_id_v2": 126880,
"warnings": {
"external_id": "User found in the company with other external_ids: ['abc-123']"
}
},
"information": {
"address": { "text": "DOMICILIO/ADDRESS, HARLINGEN, TX 78552", "valid": false },
"birthdate": { "text": "14/11/1956", "valid": true },
"complete_name": { "text": "LUKE SKYWALKER", "valid": true },
"curp": { "text": "GOCJ850627HDFRRL09", "valid": true },
"electoral_key": { "text": "GRCRSN82031007M500", "valid": true },
"last_name": { "text": "SKYWALKER", "valid": true },
"mother_last_name": { "text": "ORGANA", "valid": true },
"name": { "text": "LUKE", "valid": true },
"registration_year": { "text": "1998", "valid": true },
"sex": { "text": "H", "valid": true },
"valid_thru": { "text": "2027", "valid": true }
}
}
},
"face_match": false,
"label": null,
"reason": null,
"request_id": "d1kxp9ah8f0s71uv9zx0",
"selfie": {
"face_id": 237436,
"face_id_v2": 4378,
"first_seen": "02/05/2025, 02:36:19",
"first_seen_image": true,
"inquiry_date": "07/28/2025, 20:52:49",
"last_seen": "07/28/2025, 20:52:51",
"last_seen_by_your_company": "07/23/2025, 18:14:27",
"match": true,
"match_fraud_flag": true,
"seen_by_your_company": true,
"seen_different_companies": 2,
"times_seen_by_your_company": 2,
"times_seen_last_month": 7,
"unique_face_id_v2": 494,
"warnings": {}
},
"unico": {
"process_id": "d333dfac-9ddb-4066-8e2c-44eaf4c86b4a",
"result": "PROCESS_RESULT_LIVE"
}
},
"user_id": ""
},
"request_date": "2025-07-28T20:53:38",
"status": "Request fulfilled, document follows",
"status_code": 200,
"version": "v3.6.0"
}
Campos de la respuesta:
Nivel raíz:
| Campo | Tipo | Descripción |
|---|---|---|
status | string | Representación textual del estado de la respuesta. |
status_code | integer | Código de estado HTTP. |
request_date | string (date-time) | La fecha y hora en que se realizó la solicitud. |
version | string | Versión de la API que procesó la solicitud. |
data.user_id | string | El ID de usuario establecido en la solicitud original. |
data.images — Imágenes capturadas en Base64:
| Campo | Tipo | Descripción |
|---|---|---|
data.images.document_image | string | Imagen en Base64 del frente del documento. |
data.images.document_image_back | string | Imagen en Base64 del reverso del documento. |
data.images.selfie | string | Imagen de selfie en Base64 capturada durante la verificación. |
data.response.unico — Veredicto consolidado:
| Campo | Tipo | Descripción |
|---|---|---|
data.response.unico.process_id | string (UUID) | Identificador interno relacionado con el proceso de verificación. |
data.response.unico.result | string | Resultado del proceso. Los valores posibles se describen a continuación. |
Valores posibles para data.response.unico.result:
El valor del resultado codifica una de tres dimensiones de evaluación:
- Evaluación de identidad — si el rostro capturado pertenece al titular del documento (
PROCESS_RESULT_VERIFIED,PROCESS_RESULT_NOT_APPROVED). - Comportamiento de fraude — si las señales de comportamiento o de red indican riesgo de fraude (
PROCESS_RESULT_LIVE,PROCESS_RESULT_HIGH_RISK,PROCESS_RESULT_CRITICAL_RISK,PROCESS_RESULT_NOT_APPROVED). - Liveness — si se detectó un rostro vivo en el momento de la captura (
PROCESS_RESULT_NOT_LIVE).
| Resultado | Recomendación | Significado | Señal |
|---|---|---|---|
PROCESS_RESULT_ERROR | Rechazar | El proceso finalizó con error. | Sistema |
PROCESS_RESULT_VERIFIED | Aceptar | El usuario estaba activo en el momento de la captura; es el rostro del titular del documento y no se encontró evidencia relacionada con fraude. | Identidad: confirmada |
PROCESS_RESULT_LIVE | Revisar / Aceptar | El usuario estaba activo en el momento de la captura; no encontramos evidencia suficiente para garantizar que es el titular del documento y no hay evidencia de fraude. | Identidad: no concluyente · Fraude: ninguno |
PROCESS_RESULT_HIGH_RISK | Recomienda rechazo | Encontramos al menos una evidencia sólida de fraude. La decisión final es suya. | Comportamiento de fraude: 1 señal fuerte |
PROCESS_RESULT_CRITICAL_RISK | Recomienda rechazo | Encontramos al menos 2 evidencias sólidas de fraude. La decisión final es suya. | Comportamiento de fraude: 2+ señales fuertes |
PROCESS_RESULT_NOT_APPROVED | Rechazar | Se recomienda el rechazo ya que se detectaron múltiples indicios de fraude. | Identidad: rechazada · Comportamiento de fraude: múltiples señales |
PROCESS_RESULT_NOT_LIVE | Permitir hasta 2 reintentos | El usuario no estaba vivo en el momento de la captura, aunque no encontramos otros indicios de fraude. | Liveness: rostro no vivo |
PROCESS_RESULT_VERIFIED requiere activación. Por defecto, este resultado no está activo — consulte con su Project Manager de Unico si desea habilitarlo.
Señales auxiliares para su decisión:
Para los casos LIVE, HIGH_RISK o CRITICAL_RISK, complemente su decisión con estos campos:
| Campo | Tipo | Significado |
|---|---|---|
face_match | boolean | Estado de coincidencia facial entre documento y selfie. |
document.front.face_analysis.match_fraud_flag | boolean | Si el rostro del documento ha sido asociado con actividad fraudulenta. |
selfie.match_fraud_flag | boolean | Si el rostro de la selfie ha sido asociado con actividad fraudulenta. |
warnings.external_id | string | Advertencia que indica que el rostro del usuario está asociado con múltiples IDs externos. |
data.response — Señales de resultado de nivel superior:
| Campo | Tipo | Descripción |
|---|---|---|
data.response.face_match | boolean | Si el rostro de la selfie coincide con el rostro del documento. |
data.response.label | string (nullable) | Reservado para resultados de sub-procesos específicos; null si no aplica. |
data.response.reason | string (nullable) | Razones para la etiqueta asignada. |
data.response.request_id | string | Identificador único para la solicitud de API. |
data.response.curp — Validación del CURP contra RENAPO:
| Campo | Tipo | Descripción |
|---|---|---|
curp | string | La cadena CURP que se está analizando. |
government_valid | boolean | Si el CURP es válido según RENAPO. |
government_name | string | Nombre completo asociado al CURP en la base de datos oficial. |
name_to_CURP_valid | boolean | Si el nombre proporcionado es consistente con el CURP. |
is_mexican | boolean | Si el CURP corresponde a un ciudadano mexicano. |
deceased | boolean | Si el CURP está marcado como fallecido en la base de datos oficial. |
date_of_birth | string | Fecha de nacimiento decodificada del CURP (DD/MM/AAAA). |
age | integer | Edad calculada del usuario. |
gender | string | Género decodificado del CURP (M o F). |
state_of_birth | string | Estado de nacimiento decodificado del CURP. |
state_iso | string | Código ISO 3166-2 del estado de nacimiento. |
data.response.document.details — Detección y análisis forense del documento:
| Campo | Tipo | Descripción |
|---|---|---|
detected | boolean | Si se detectó un documento exitosamente en la imagen. |
document_id | integer | Identificador único del documento procesado. |
forensics.is_valid | string | Resultado del análisis forense: yes, no o inconclusive. |
data.response.document.front.information — Campos extraídos por OCR del frente del INE. Cada campo contiene text (valor extraído) y valid (si pudo ser validado estructuralmente):
| Campo | Descripción |
|---|---|
name | Nombre(s). |
last_name | Apellido paterno. |
mother_last_name | Apellido materno. |
complete_name | Nombre completo. |
birthdate | Fecha de nacimiento (DD/MM/AAAA). |
sex | Sexo/género (H o M). |
curp | CURP extraído del documento. |
electoral_key | Clave de elector. |
address | Domicilio. |
registration_year | Año de registro del documento. |
valid_thru | Año de vencimiento del documento. |
data.response.document.front.face_analysis — Análisis del rostro encontrado en el INE:
| Campo | Tipo | Descripción |
|---|---|---|
face_id | integer | Identificador único de esta instancia de rostro. |
face_id_v2 | integer | Identificador único (versión 2). |
unique_face_id_v2 | integer | Identificador persistente para este rostro físico en todas las consultas. |
first_seen | string | Marca de tiempo de la primera vez que se vio este rostro en el sistema. |
last_seen | string | Marca de tiempo de la última vez que se vio este rostro en la red. |
last_seen_by_your_company | string | La vez más reciente que este rostro fue visto en una consulta de su empresa. |
inquiry_date | string | Marca de tiempo de la consulta de verificación actual. |
match | boolean | Si este rostro es una posible coincidencia con otros rostros en la base de datos. |
match_fraud_flag | boolean | Si este rostro ha sido asociado con actividad fraudulenta. |
seen_by_your_company | boolean | Si este rostro fue visto anteriormente por su empresa. |
seen_different_companies | integer | Número de otras empresas en la red que han visto este rostro. |
times_seen_by_your_company | integer | Total de veces que este rostro fue visto en consultas de su empresa. |
times_seen_last_month | integer | Número de veces visto el último mes en toda la red. |
warnings.external_id | string | Advertencia si el rostro del usuario está asociado con múltiples IDs externos. |
data.response.document.back — Datos MRZ del reverso del INE:
| Campo | Tipo | Descripción |
|---|---|---|
mrz | string | Cadena completa de la Zona de Lectura Mecánica (MRZ). |
cic | string | Número CIC extraído del MRZ. |
citizen_id | string | Número de identificación ciudadana del MRZ. |
data.response.selfie — Análisis de la selfie capturada:
| Campo | Tipo | Descripción |
|---|---|---|
face_id | integer | Identificador único de la instancia del rostro en la selfie. |
face_id_v2 | integer | Identificador único (versión 2). |
unique_face_id_v2 | integer | Identificador persistente para este rostro físico en todas las consultas. |
first_seen | string | Primera vez que el rostro de esta selfie fue visto en el sistema. |
first_seen_image | boolean | Si es la primera vez que se ve esta imagen exacta. |
last_seen | string | La vez más reciente que este rostro fue visto en la red. |
last_seen_by_your_company | string | La vez más reciente que este rostro fue visto por su empresa. |
inquiry_date | string | Marca de tiempo de la consulta de verificación actual. |
match | boolean | Si el rostro de la selfie coincide con otros rostros en la base de datos. |
match_fraud_flag | boolean | Si el rostro de la selfie está asociado con actividad fraudulenta. |
seen_by_your_company | boolean | Si este rostro fue visto anteriormente por su empresa. |
seen_different_companies | integer | Número de otras empresas en la red que han visto este rostro. |
times_seen_by_your_company | integer | Total de veces que este rostro fue visto en consultas de su empresa. |
times_seen_last_month | integer | Número de veces visto el último mes en toda la red. |
warnings | object | Advertencias específicas relacionadas con el análisis de la selfie (misma estructura que face_analysis del documento). |
Use polling con backoff exponencial — no llame en un bucle cerrado.
Respuestas de error — GET /v2/history/request
| Código | Mensaje | Descripción |
|---|---|---|
400 Bad Request | Input should be a valid UUID, invalid group length... | El parámetro magic_link_token está malformado o no tiene un formato UUID válido. |
404 Not Found | This magic link have no requests associated | El token proporcionado existe pero no tiene procesos KYC completados vinculados a él. Es posible que el usuario no haya terminado el journey. |
500 Internal Server Error | internal server error | Fallo de procesamiento en el servidor. Reintente con backoff exponencial. Si persiste, repórtelo al soporte. |
Ejemplo de respuesta 400:
{
"data": {
"error": [
{
"attribute": ["magic_link_token"],
"message": "Input should be a valid UUID, invalid group length in group 4: expected 12, found 11",
"type": "uuid_parsing"
}
]
},
"request_date": "2025-07-28T20:43:34",
"status": "There are some errors in the request",
"status_code": 400,
"version": "v3.6.0"
}
Ejemplo de respuesta 404:
{
"data": {
"error": "This magic link have no requests associated"
},
"request_date": "2025-07-28T20:40:50",
"status": "Nothing matches the given URI",
"status_code": 404,
"version": "v3.6.0"
}
Ejemplo de respuesta 500:
{
"data": {
"error": "internal server error"
},
"version": "v1.4.2",
"status": "bad request",
"status_code": 400,
"request_date": "2025-07-28T20:22:29+0000",
"request": {
"metadata": null
}
}
Webhook
Hay tres eventos de webhook disponibles. Para activarlos, solicite la configuración a su Project Manager de Onboarding proporcionando: URL del endpoint (HTTPS obligatorio), tipo de autenticación (basic_auth, api_key, oauth2 o NoAuth), configuración de autenticación, intentos máximos de reintento, intervalo de reintento (s) y tiempo de espera (s).
Este evento se envía al final del flujo, cuando el Decision Maker ha terminado de procesar la información enviada.
{
"data": {
"images": {
"document_image": "base64str",
"document_image_back": "base64str",
"selfie": "base64str"
},
"response": {
"document": {
"details": {
"detected": true,
"forensics": {
"is_valid": "yes"
},
"document_id": 123
},
"front": {
"information": {
"birthdate": { "text": "14/11/1956", "valid": true },
"sex": { "text": "H", "valid": true },
"registration_year": { "text": "1998", "valid": true },
"name": { "text": "LUKE", "valid": true },
"mother_last_name": { "text": "ORGANA", "valid": true },
"last_name": { "text": "SKYWALKER", "valid": true },
"electoral_key": { "text": "GRCRSN82031007M500", "valid": true },
"curp": { "text": "GOCJ850627HDFRRL09", "valid": true },
"address": { "text": "DOMICILIO/ADDRESS, HARLINGEN, TX 78552", "valid": false },
"complete_name": { "text": "LUKE SKYWALKER", "valid": true },
"valid_thru": { "text": "2027", "valid": true }
},
"face_analysis": {
"face_id": 237437,
"first_seen": "12/22/2022, 18:54:09",
"unique_face_id_v2": 126880,
"face_id_v2": 199068,
"inquiry_date": "07/28/2025, 20:53:12",
"last_seen": "07/28/2025, 18:47:46",
"last_seen_by_your_company": "07/24/2025, 21:38:21",
"match": true,
"match_fraud_flag": true,
"seen_by_your_company": true,
"seen_different_companies": 46,
"times_seen_by_your_company": 3,
"times_seen_last_month": 111,
"warnings": {
"external_id": "User found in the company with other external_ids: ['abc-123']"
}
}
},
"back": {
"mrz": "IDMEX999999999999<9 VADER<SKYWALKER<<LUKE",
"cic": "237457894"
}
},
"selfie": {
"face_id": 237436,
"first_seen": "02/05/2025, 02:36:19",
"unique_face_id_v2": 494,
"face_id_v2": 4378,
"inquiry_date": "07/28/2025, 20:52:49",
"last_seen": "07/28/2025, 20:52:51",
"last_seen_by_your_company": "07/23/2025, 18:14:27",
"match": true,
"match_fraud_flag": true,
"seen_by_your_company": true,
"seen_different_companies": 2,
"times_seen_by_your_company": 2,
"times_seen_last_month": 7,
"warnings": {
"external_id": "User found in the company with other external_ids: ['abc-123']"
},
"first_seen_image": true
},
"face_match": false,
"curp": {
"curp": "GOCJ850627HDFRRL09",
"state_of_birth": "Nuevo León",
"state_iso": "MX-NLE",
"date_of_birth": "14/11/1956",
"age": 58,
"gender": "M",
"is_mexican": true,
"name_to_CURP_valid": true,
"government_valid": true,
"government_name": "LUKE SKYWALKER",
"deceased": false
},
"unico": {
"process_id": "d333dfac-9ddb-4066-8e2c-44eaf4c86b4a",
"result": "PROCESS_RESULT_LIVE"
},
"label": null,
"reason": null,
"request_id": "d1kxp9ah8f0s71uv9zx0"
},
"user_id": null
},
"event": "MAGIC_LINK_RESULTS",
"magic_link_token": "3f6dbcc1-49ba-4935-be90-dd8dd59b5530",
"user_id": null,
"date": "2025-10-03T21:15:41.299815"
}
Estos eventos se reciben cuando el usuario completó una acción. Por ejemplo, form_start se recibirá cuando el usuario esté tomando el frente del documento, lo que significa que el usuario hizo clic y completó la primera pantalla.
{
"data": {
"completed_on": "Fri, 03 Oct 2025 21:15:35 GMT",
"started_on": "Fri, 03 Oct 2025 21:15:30 GMT",
"step": "form_start",
"user_id": null
},
"event": "MAGIC_LINK_TRACK",
"magic_link_token": "3f6dbcc1-49ba-4935-be90-dd8dd59b5530",
"user_id": null,
"date": "2025-10-03T21:15:41.299815"
}
| Campo | Tipo | Descripción |
|---|---|---|
data.step | string | Nombre del paso completado. |
data.user_id | string (nullable) | ID externo establecido en el magic link. |
data.started_on | datetime | Hora de inicio del paso en zona horaria UTC. |
data.completed_on | datetime | Hora de finalización del paso en zona horaria UTC. |
event | string | Identificador del tipo de evento. |
magic_link_token | uuid | Token único del magic link. |
user_id | string (nullable) | Identificador externo. |
date | datetime | Marca de tiempo del evento. |
Pasos compatibles:
| Paso | Descripción |
|---|---|
form_start | El usuario hizo clic en el botón inicial del magic link. |
form_document_front | El usuario completó la captura del frente del INE. |
form_document_back | El usuario completó la captura del reverso del INE. |
form_document | El usuario completó los procesos del frente y el reverso. |
form_selfie | El usuario completó el proceso de liveness. |
form_decision_maker | El usuario completó el flujo completo. |
Este evento se envía cuando se requiere una nueva captura del documento, ya sea durante el proceso del frente o del reverso. Indica que no se pudo leer información crítica del INE.
{
"data": {
"document_type": "ine_front",
"invalid_back_ocr": false,
"invalid_curp": false,
"invalid_document": true
},
"event": "MAGIC_LINK_DOCUMENT_RETAKE_REASONS",
"magic_link_token": "3f6dbcc1-49ba-4935-be90-dd8dd59b5530",
"user_id": null,
"date": "2025-10-03T21:12:00.000Z"
}
| Campo | Tipo | Descripción |
|---|---|---|
data.document_type | string | Cara del documento: ine_front o ine_back. |
data.invalid_document | boolean | true si la imagen capturada no es un INE válido (cualquier cara). |
data.invalid_curp | boolean | true si el CURP no pudo ser leído (solo ine_front). |
data.invalid_back_ocr | boolean | true si el código MRZ no pudo ser leído (solo ine_back). |
event | string | Identificador del tipo de evento. |
magic_link_token | uuid | Token único del magic link. |
user_id | string (nullable) | Identificador externo. |
date | datetime | Marca de tiempo del evento. |
El payload está diseñado para que solo se reciba un error (true) por cada cara del documento.
Escenarios de fallo — frente del INE:
- Documento inválido → solo
invalid_document: true - Fallo en lectura del CURP → solo
invalid_curp: true
Escenarios de fallo — reverso del INE:
- Documento inválido → solo
invalid_document: true - Fallo en lectura del MRZ → solo
invalid_back_ocr: true
Si todavía envía webhook_url en los metadata de la solicitud (Webhook V1), esa configuración tiene prioridad sobre la V2 global. Para migrar, elimine webhook_url de metadata y configure V2 con su PM de Onboarding.
Personalizaciones
La página del Magic Link alojado no admite personalización visual por parte del cliente — sigue la identidad visual predeterminada de Unico/Trully. Para personalizar el journey (logo, colores, textos), use Onboarding (Global) con SDK o Web.
Disponibilidad: México · Endpoint: POST https://api.trully.ai/v2/magic-link · Documento: INE · Auth: x-api-key (Trully)