KYC Magic Link
This use case does not use the POST /v1/process Unico endpoint nor the API/TCA contract. The integration is with Trully.ai (host api.trully.ai), with authentication via x-api-key instead of Bearer JWT, and a proprietary response schema. For other countries, use Onboarding (Global).
What this use case solves
KYC Magic Link addresses the challenge of executing the identification process in Mexico, collecting national identity documents (INE) and facial biometrics. With a journey hosted by Unico, you eliminate front-end development friction through a link sent via your own channels (WhatsApp, SMS, email).
Use this use case when:
- You operate in Mexico and the identity document used is the INE (mandatory).
Do not use this use case when:
- The user is outside Mexico or uses other documents → look at the other onboarding use cases.
Capabilities involved
Pipeline executed within a single process:
| Capability | Required | Role in the flow |
|---|---|---|
| Document Capture | Required | Captures the INE document image. Document reuse is not available in this use case — each session requires a new capture. |
| Liveness | Required | Liveness check — mandatory selfie that anchors the process. |
| Risk Fraud Classification | Required | Cross-references behavioral signals to flag fraud risk associated with the CPF. |
| Identity Verification | Optional (if contracted) | Verifies whether the transaction face belongs to the holder of the provided government identifier, using Unico's identity base and additional signals. |
Prerequisites
- API key — provisioned by your Onboarding Project Manager at Unico. Sent in the
x-api-keyheader. - Public HTTPS endpoint to receive webhooks (optional, but recommended).
- CORS configuration — allow origins
https://verification.unico.app(production) andhttps://verification.uat.unico.app(sandbox) on the server that receives the webhook.
Step-by-step implementation
Unlike other use cases, Magic Link does not have a flow field — the integration is direct with the Trully API. The endpoint creates a unique verification link that you distribute to the user through your own channel.
Endpoint: POST https://sandbox.trully.ai/v2/magic-link
Headers:
| Header | Required | Description |
|---|---|---|
x-api-key | Yes | API key provisioned by your Onboarding Project Manager at Unico. |
Content-Type | Yes | application/json |
Body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
external_id | string | No | An external identifier for the request, used for tracking and reference purposes. |
metadata | object | No | Container for configuration parameters. |
metadata.phone | string | No | User's phone number in international format (e.g., 521234567890). |
metadata.redirect_url | string | No | URL to redirect the user after the KYC process is completed. |
metadata.webhook_url | string | No | URL to send the KYC process data after the KYC process is completed. This webhook will be called in the client side. For security reasons the endpoint should use HTTPS. The component will wait one minute for your webhook server to respond — after that, it will drop the communication. The process won't be affected in any way by webhook communication. Make sure to allow https://verification.unico.app and https://verification.uat.unico.app in your CORS configuration for production and sandbox respectively. |
metadata.track_webhook_url | string | No | URL to send each KYC step processed by the user (see webhook events below). This webhook will be called in the client side. For security reasons the endpoint should use HTTPS. The component will wait one minute for your webhook server to respond — after that, it will drop the communication. The process won't be affected in any way by webhook communication. Make sure to allow https://verification.unico.app and https://verification.uat.unico.app in your CORS configuration for production and sandbox respectively. |
Possible values for data.step (sent to track_webhook_url):
| Step | Description |
|---|---|
form_start | User is currently scanning the document. |
form_document_front | The user has already captured the INE Front. |
form_document_back | The user has already captured the INE Back. |
form_document | Process is currently in the selfie step. |
form_selfie | User reached the final step of the operation. |
form_decision_maker | The operation returns the system response. |
Example request:
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"
}
}'
Response fields:
| Field | Type | Description |
|---|---|---|
data.external_id | string (nullable) | An external identifier for the request, used for tracking and reference purposes. |
data.created_on | string (date-time) | The date when the magic link was created. |
data.is_active | boolean | If true, the magic link is active. |
data.token | string | The token of the magic link which will be used to link the KYC processes to the magic link. |
data.magic_link_url | string (URI) | The magic URL link to start the KYC process. |
data.metadata.redirect_url | string (nullable) | URL to redirect the user after the KYC process is completed. |
data.metadata.webhook_url | string (nullable) | URL to send the KYC process data after the KYC process is completed. This webhook will be called in the client side. |
data.metadata.track_webhook_url | string (nullable) | URL to send each KYC step processed by the user. This webhook will be called in the client side. |
data.version | string | The version of the magic link. |
version | string | The version of the API that processed the request, useful for tracking changes and compatibility. |
status | string | A textual representation of the response status, indicating success or failure of the operation. |
status_code | integer | The HTTP status code of the response, providing a standardized indicator of the request's outcome. |
request_date | string (date-time) | The date and time when the request was made, in ISO 8601 format. |
request.metadata.redirect_url | string (nullable) | URL to redirect the user after the KYC process is completed (echo of the request). |
request.metadata.webhook_url | string (nullable) | URL to send the KYC process data after completion (echo of the request). |
request.metadata.track_webhook_url | string (nullable) | URL to send each KYC step processed by the user (echo of the request). |
Example response:
{
"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
}
}
}
Error responses — POST /v2/magic-link
| Code | Message | Description |
|---|---|---|
400 Bad Request | data provided in the field is invalid | Invalid data structure or field values in the request payload. Check required fields and formats. |
403 Forbidden | Forbidden | API key missing, expired or without permission. Verify the x-api-key header. |
500 Internal Server Error | internal server error | Server-side processing failure. Retry with exponential backoff. If persistent, report to support. |
Example 400 response:
{
"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
}
}
Send via WhatsApp, SMS, email or embed. The user accesses the link on their own device and completes the hosted journey.
- Polling via GET (required) — call
GET /v2/history/request?magic_link_token={token}periodically untilunico.resultis populated. See Polling via GET below. - Webhook (optional) — configure with your Onboarding PM to receive events automatically. See Webhook below.
Polling via GET
Endpoint: GET https://sandbox.trully.ai/v2/history/request
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
magic_link_token | string | Yes | Token returned when the Magic Link was created. |
Headers:
| Header | Required | Description |
|---|---|---|
x-api-key | Yes | API key provisioned by your Onboarding Project Manager at Unico. |
Example request:
curl "https://sandbox.trully.ai/v2/history/request?magic_link_token=$TOKEN" \
-H "x-api-key: $TRULLY_API_KEY"
Example response:
{
"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"
}
Response fields:
Root level:
| Field | Type | Description |
|---|---|---|
status | string | Textual representation of the response status. |
status_code | integer | HTTP status code. |
request_date | string (date-time) | The date and time when the request was made. |
version | string | API version that processed the request. |
data.user_id | string | The user ID set in the original request. |
data.images — Base64-encoded captured images:
| Field | Type | Description |
|---|---|---|
data.images.document_image | string | Base64-encoded image of the front of the document. |
data.images.document_image_back | string | Base64-encoded image of the back of the document. |
data.images.selfie | string | Base64-encoded selfie image captured during verification. |
data.response.unico — Consolidated verdict:
| Field | Type | Description |
|---|---|---|
data.response.unico.process_id | string (UUID) | Internal identifier related to the verification process. |
data.response.unico.result | string | Process result. Possible values described below. |
Possible values for data.response.unico.result:
The result value encodes one of three evaluation dimensions:
- Identity assessment — whether the captured face belongs to the document holder (
PROCESS_RESULT_VERIFIED,PROCESS_RESULT_NOT_APPROVED). - Fraud behavior — whether behavioral or network signals indicate fraud risk (
PROCESS_RESULT_LIVE,PROCESS_RESULT_HIGH_RISK,PROCESS_RESULT_CRITICAL_RISK,PROCESS_RESULT_NOT_APPROVED). - Liveness — whether a live face was detected at capture time (
PROCESS_RESULT_NOT_LIVE).
| Result | Recommendation | Meaning | Signal |
|---|---|---|---|
PROCESS_RESULT_ERROR | Reject | Process finished with error. | System |
PROCESS_RESULT_VERIFIED | Accept | The user was active at the time of capture; it is the face of the ID holder and no evidence related to fraud was found. | Identity: confirmed |
PROCESS_RESULT_LIVE | Review / Accept | The user was active at the time of the capture; we did not find sufficient evidence to guarantee that they are the ID holder and no evidence of fraud. | Identity: inconclusive · Fraud: none |
PROCESS_RESULT_HIGH_RISK | Recommend rejection | We found at least one strong evidence of fraud. Final decision is yours. | Fraud behavior: 1 strong signal |
PROCESS_RESULT_CRITICAL_RISK | Recommend rejection | We found at least 2 strong evidences of fraud. Final decision is yours. | Fraud behavior: 2+ strong signals |
PROCESS_RESULT_NOT_APPROVED | Reject | Rejection recommended as multiple indications of fraud were detected. | Identity: rejected · Fraud behavior: multiple signals |
PROCESS_RESULT_NOT_LIVE | Allow up to 2 retries | The user was not live at the time of capture, although we found no other indications of fraud. | Liveness: face not live |
PROCESS_RESULT_VERIFIED requires activation. By default, this result is not active — align with your Unico Project Manager if you want to enable it.
Auxiliary signals for your decision:
For LIVE, HIGH_RISK or CRITICAL_RISK cases, complement your decision with these fields:
| Field | Type | Meaning |
|---|---|---|
face_match | boolean | Face matching status between document and selfie. |
document.front.face_analysis.match_fraud_flag | boolean | Whether the document face has been associated with fraudulent activity. |
selfie.match_fraud_flag | boolean | Whether the selfie face has been associated with fraudulent activity. |
warnings.external_id | string | Warning indicating the user's face is associated with multiple external IDs. |
data.response — Top-level result signals:
| Field | Type | Description |
|---|---|---|
data.response.face_match | boolean | Whether the selfie face matches the document face. |
data.response.label | string (nullable) | Reserved for results from specific sub-processes; null if not applicable. |
data.response.reason | string (nullable) | Reasons for the assigned label. |
data.response.request_id | string | Unique identifier for the API request. |
data.response.curp — CURP validation against RENAPO:
| Field | Type | Description |
|---|---|---|
curp | string | The CURP string being analyzed. |
government_valid | boolean | Whether the CURP is valid according to RENAPO. |
government_name | string | Full name associated with the CURP in the official database. |
name_to_CURP_valid | boolean | Whether the provided name is consistent with the CURP. |
is_mexican | boolean | Whether the CURP corresponds to a Mexican citizen. |
deceased | boolean | Whether the CURP is marked as deceased in the official database. |
date_of_birth | string | Date of birth as decoded from the CURP (DD/MM/YYYY). |
age | integer | Calculated age of the user. |
gender | string | Gender as decoded from the CURP (M or F). |
state_of_birth | string | State of birth as decoded from the CURP. |
state_iso | string | ISO 3166-2 code for the state of birth. |
data.response.document.details — Document detection and forensics:
| Field | Type | Description |
|---|---|---|
detected | boolean | Whether a document was successfully detected in the image. |
document_id | integer | Unique identifier for the processed document. |
forensics.is_valid | string | Forensic analysis result: yes, no or inconclusive. |
data.response.document.front.information — OCR-extracted fields from the INE front. Each field contains text (extracted value) and valid (whether it could be structurally validated):
| Field | Description |
|---|---|
name | First name(s). |
last_name | Paternal last name. |
mother_last_name | Maternal last name. |
complete_name | Full name. |
birthdate | Date of birth (DD/MM/YYYY). |
sex | Sex/gender (H or M). |
curp | CURP extracted from the document. |
electoral_key | Electoral key. |
address | Address. |
registration_year | Year the document was registered. |
valid_thru | Document expiration year. |
data.response.document.front.face_analysis — Analysis of the face found on the INE:
| Field | Type | Description |
|---|---|---|
face_id | integer | Unique identifier for this face instance. |
face_id_v2 | integer | Unique identifier (version 2). |
unique_face_id_v2 | integer | Persistent identifier for this physical face across all inquiries. |
first_seen | string | Timestamp of the first time this face was ever seen in the system. |
last_seen | string | Timestamp of the most recent time this face was seen in the network. |
last_seen_by_your_company | string | Most recent time this face was seen in an inquiry by your company. |
inquiry_date | string | Timestamp of the current verification inquiry. |
match | boolean | Whether this face is a potential match with other faces in the database. |
match_fraud_flag | boolean | Whether this face has been associated with fraudulent activity. |
seen_by_your_company | boolean | Whether this face was previously seen by your company. |
seen_different_companies | integer | Number of other companies in the network that have seen this face. |
times_seen_by_your_company | integer | Total times this face was seen in inquiries by your company. |
times_seen_last_month | integer | Number of times seen in the last month across the network. |
warnings.external_id | string | Warning if the user's face is associated with multiple external IDs. |
data.response.document.back — MRZ data from the INE back:
| Field | Type | Description |
|---|---|---|
mrz | string | Full Machine-Readable Zone string. |
cic | string | CIC number extracted from the MRZ. |
citizen_id | string | Citizen identification number from the MRZ. |
data.response.selfie — Analysis of the captured selfie:
| Field | Type | Description |
|---|---|---|
face_id | integer | Unique identifier for the selfie face instance. |
face_id_v2 | integer | Unique identifier (version 2). |
unique_face_id_v2 | integer | Persistent identifier for this physical face across all inquiries. |
first_seen | string | First time this selfie's face was ever seen in the system. |
first_seen_image | boolean | Whether this is the first time this exact image was seen. |
last_seen | string | Most recent time this face was seen in the network. |
last_seen_by_your_company | string | Most recent time this face was seen by your company. |
inquiry_date | string | Timestamp of the current verification inquiry. |
match | boolean | Whether the selfie face matches other faces in the database. |
match_fraud_flag | boolean | Whether the selfie face is associated with fraudulent activity. |
seen_by_your_company | boolean | Whether this face was previously seen by your company. |
seen_different_companies | integer | Number of other companies in the network that have seen this face. |
times_seen_by_your_company | integer | Total times this face was seen in inquiries by your company. |
times_seen_last_month | integer | Number of times seen in the last month across the network. |
warnings | object | Specific warnings related to selfie analysis (same structure as document face_analysis). |
Use polling with exponential backoff — do not call in a tight loop.
Error responses — GET /v2/history/request
| Code | Message | Description |
|---|---|---|
400 Bad Request | Input should be a valid UUID, invalid group length... | The magic_link_token parameter is malformed or not a valid UUID format. |
404 Not Found | This magic link have no requests associated | The provided token exists but has no completed KYC processes linked to it. The user may not have finished the journey yet. |
500 Internal Server Error | internal server error | Server-side processing failure. Retry with exponential backoff. If persistent, report to support. |
Example 400 response:
{
"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"
}
Example 404 response:
{
"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"
}
Example 500 response:
{
"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
Three webhook events are available. To activate, request setup from your Onboarding Project Manager providing: endpoint URL (HTTPS required), auth type (basic_auth, api_key, oauth2 or NoAuth), auth configuration, max retry attempts, retry interval (s) and timeout (s).
This event is sent at the end of the flow, when the Decision Maker has finished processing the information sent.
{
"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"
}
These events are received when the user completed an action. For example, form_start will be received when the user is taking the front document, which means that the user clicked and completed the first screen.
{
"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"
}
| Field | Type | Description |
|---|---|---|
data.step | string | Name of the completed step. |
data.user_id | string (nullable) | External ID set in the magic link. |
data.started_on | datetime | UTC timezone step start time. |
data.completed_on | datetime | UTC timezone step completion time. |
event | string | Event type identifier. |
magic_link_token | uuid | Unique magic link token. |
user_id | string (nullable) | External identifier. |
date | datetime | Event timestamp. |
Supported steps:
| Step | Description |
|---|---|
form_start | User clicked the initial magic link button. |
form_document_front | User completed INE front capture. |
form_document_back | User completed INE back capture. |
form_document | User completed both front and back processes. |
form_selfie | User completed the liveness process. |
form_decision_maker | User completed the entire flow. |
This event is sent when a document re-capture is required, either during the front or back process. It indicates that critical data from the INE could not be read.
{
"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"
}
| Field | Type | Description |
|---|---|---|
data.document_type | string | Document side: ine_front or ine_back. |
data.invalid_document | boolean | true if the captured image is not a valid INE (either side). |
data.invalid_curp | boolean | true if the CURP could not be read (ine_front only). |
data.invalid_back_ocr | boolean | true if the MRZ code could not be read (ine_back only). |
event | string | Event type identifier. |
magic_link_token | uuid | Unique magic link token. |
user_id | string (nullable) | External identifier. |
date | datetime | Event timestamp. |
The payload is designed so that only one error (true) is received for each document side.
Failure scenarios — INE front:
- Invalid document → only
invalid_document: true - CURP reading failure → only
invalid_curp: true
Failure scenarios — INE back:
- Invalid document → only
invalid_document: true - MRZ reading failure → only
invalid_back_ocr: true
If you still send webhook_url in the metadata of the request (Webhook V1), that configuration takes precedence over the global V2. To migrate, remove webhook_url from metadata and configure V2 with your Onboarding PM.
Customizations
The hosted Magic Link page does not support visual customization by the client — it follows the default Unico/Trully visual identity. To customize the journey (logo, colors, texts), use Onboarding (Global) with SDK or Web.
Availability: Mexico · Endpoint: POST https://api.trully.ai/v2/magic-link · Document: INE · Auth: x-api-key (Trully)