PINRUT Verificación de Identidad
El servicio PINRUT permite verificar la identidad de personas mediante un PIN numérico personal vinculado a su RUT chileno. La persona crea su PIN una sola vez y lo usa para autorizar operaciones solicitadas por tu sistema.
Casos de uso típicos:
- Firma de contratos o documentos
- Autorización de transacciones financieras
- Confirmación de identidad en procesos de onboarding
- Validación de operaciones sensibles
Requisitos Previos
Sección titulada «Requisitos Previos»Antes de usar la API necesitas:
- Una API Key con rol
FULL-API - El tenant debe tener el servicio PIN_RUT habilitado
- Una integración PINRUT configurada con las URLs permitidas
Configuración de Integraciones
Sección titulada «Configuración de Integraciones»Antes de crear transacciones, debes configurar una integración desde el panel de administración:
Configuración → PINRUT → Integraciones
Campos de la Integración
Sección titulada «Campos de la Integración»| Campo | Descripción |
|---|---|
| Slug | Identificador único inmutable (ej: mi_app_prod). Se usa en cada request. |
| Nombre | Nombre descriptivo que la persona verá en la pantalla de verificación |
| URIs de redirect | URLs HTTPS donde el browser redirige tras completar la verificación |
| URLs de callback | URLs HTTPS donde se envían webhooks con el resultado |
| Orígenes iframe | URLs HTTPS autorizadas para embeber la pantalla PIN en un iframe |
| URI de redirect default | Redirect por defecto si no se especifica en cada transacción |
Datos personales que recibe (sharedPersonalData) | Los datos de la persona que la integración recibe de cada transacción autorizada. Hoy sólo full_name, el nombre completo leído de la cédula. Apagado por defecto |
Recibir el nombre de la persona
Sección titulada «Recibir el nombre de la persona»Si tu integración necesita identificar a la persona en la operación o documento que autorizó, puedes activar
Nombre completo leído de la cédula en la integración (en la API, sharedPersonalData: ["full_name"] al crearla o
actualizarla). Está apagado por defecto y la finalidad es una sola: identificar a la persona en la operación o
documento que autorizó.
Con el dato activo:
- Cada transacción debe indicar su
callback_url. Sin ella, crear la transacción responde422concode: "callback_url_required". Por eso activarlo exige que la integración tenga al menos una URL de callback permitida: si no, la configuración responde400concode: "shared_personal_data_requires_callback_url". - La pantalla de autorización se lo dice a la persona antes de que autorice: «Al autorizar, compartiremos con {nombre de tu integración} tu nombre completo, tal como aparece en tu cédula, para identificarte en esta operación.»
- El valor de
operation_labelse muestra como rótulo de la tarjeta de la operación, arriba del aviso, y no dentro de una frase. Por eso puede ser una etiqueta corta del tipo de operación —«Firma electrónica simple»— y ya no necesita ser un sintagma con artículo que calce dentro del texto del aviso. - El nombre llega sólo con la transacción autorizada, en el webhook
pin.transaction.authorizedy en la consulta de resultado, comofull_name, y en el comprobante como el claimnombreCompleto. - Lo que cuenta es lo declarado al crear la transacción: si desactivas el dato antes de que la persona autorice, esa transacción no lo entrega; si lo activas después de crearla, tampoco.
Ejemplo de Integración
Sección titulada «Ejemplo de Integración»| Campo | Valor |
|---|---|
| Slug | mi_app_prod |
| Nombre | Mi Aplicación |
| Redirect URIs | https://miapp.cl/pin-callback |
| Callback URLs | https://miapp.cl/webhooks/pin |
| Redirect default | https://miapp.cl/pin-callback |
Flujo de Verificación
Sección titulada «Flujo de Verificación»Tu Sistema Redcumbre Persona │ │ │ │── POST /pin/transactions ──▶│ │ │ (Bearer token + body) │ │ │ │ │ │◀── authorize_url ──────────│ │ │ (+ transaction_id) │ │ │ │ │ │── Redirige browser ────────▶│ │ │ a authorize_url │ │ │ │── Pantalla de autorización ▶│ │ │ │ │ │◀── Se autentica: PIN o cara│ │ │ │ │◀── Webhook (callback_url) ─│ │ │ pin.transaction.authorized │ │ │ │ │ │── Redirect a redirect_uri ▶│ │ │ ?transaction_id=X │ │ │ &status=authorized │ │ │ &state=Y │ │ │ │ │── GET /result ─────────────▶│ │ │ (verificar estado final) │ │ │◀── { status: authorized } ─│ │Pasos:
- Tu sistema crea una transacción con los datos de la operación y la modalidad con que la persona autoriza
- Rediriges al usuario a la
authorize_urlretornada - La persona ve los datos de la operación y se autentica con la modalidad que pediste
- Tu sistema recibe el resultado vía webhook y/o redirect
- Opcionalmente, consultas el resultado vía API
Las dos modalidades
Sección titulada «Las dos modalidades»authentication_method | La persona autoriza con | Cuándo usarla |
|---|---|---|
pin | Su PIN de 6 dígitos. Si todavía no tiene PIN, lo crea después de autenticarse con su cara, y crearlo autoriza la operación | Operaciones frecuentes, donde el PIN basta |
face | Una prueba de vida contra el reconocimiento facial de su identidad acreditada. El PIN no interviene: no se pide, y un PIN bloqueado no la impide | Operaciones donde necesitas presencia de la persona, no sólo conocimiento |
En las dos, si la persona no tiene su identidad acreditada, la plataforma la guía primero a acreditarla —cédula por ambos lados y prueba de vida— y después sigue con la modalidad que pediste. No haces nada distinto: es la misma URL.
Crear Transacción
Sección titulada «Crear Transacción»Endpoint
Sección titulada «Endpoint»POST /{tenantSlug}/pin/transactionsHeaders
Sección titulada «Headers»Authorization: Bearer {api_key}Content-Type: application/json{ "integration_slug": "mi_app_prod", "rut": "12.345.678-9", "authentication_method": "pin", "operation_type": "firma_contrato", "operation_label": "Firmar Contrato de Servicios N° 2026-042", "operation_detail": "Contrato por 12 meses", "redirect_uri": "https://miapp.cl/pin-callback", "callback_url": "https://miapp.cl/webhooks/pin", "state": "session-abc-123"}| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
integration_slug | string | Sí | Slug de la integración configurada |
rut | string | Sí | RUT chileno de la persona natural que va a autorizar (ej: 12.345.678-9 o 12345678-9). Nunca el RUT de una empresa: ver PINRUT es sólo para personas naturales |
authentication_method | string | Sí | Con qué autoriza la persona: pin o face. No tiene valor por defecto: sin él, 400 |
operation_type | string | Sí | Tipo de operación en snake_case (máx. 50 chars) |
operation_label | string | Sí | Descripción visible para la persona (máx. 200 chars) |
operation_detail | string | No | Describe la operación que la persona va a autorizar (máx. 500 chars). No incluyas datos personales de otras personas ni datos sensibles, como información de salud. Se conserva como evidencia de la autorización y se anonimiza junto con ella. |
operation_reference | string | No | La referencia que declara tu integración para esta operación, en formato sha256:<64 hex en minúsculas> (71 chars exactos). No se le muestra a la persona. Entra en el operation_hash y, cuando la envías, en el comprobante firmado; te vuelve en el webhook y en la consulta de resultado. No se normaliza ni se recorta: cualquier otro formato responde 400. |
redirect_uri | string | No | URL HTTPS de redirect. Debe estar en las URIs permitidas de la integración |
callback_url | string | No* | URL HTTPS para webhook. Debe estar en las URLs permitidas de la integración. *Obligatorio si la integración recibe datos personales (422 callback_url_required) |
state | string | No | Valor opaco devuelto sin modificar en redirect y webhook (máx. 2048 chars) |
PINRUT es sólo para personas naturales
Sección titulada «PINRUT es sólo para personas naturales»PINRUT verifica la identidad de personas naturales: el rut que envías en el body tiene que ser el de la persona
que va a autorizar, nunca el RUT de una empresa. Si envías un RUT de persona jurídica, la creación de la transacción
responde:
HTTP/1.1 400 Bad Request{ "code": "rut_de_persona_juridica", "message": "PINRUT es sólo para personas naturales: el RUT ingresado corresponde a una persona jurídica (una empresa). Usa el RUT personal del titular, el de su cédula de identidad."}- No se crea ninguna transacción ni queda rastro de la operación: el rechazo ocurre antes de cualquier escritura, antes
de cualquier cobro y antes del
423por PIN bloqueado. - El código
rut_de_persona_juridicaes estable: tu integración puede distinguirlo de otras causas de400(ver Códigos de Error) y mostrarle a quien opera tu sistema que el RUT ingresado no corresponde a una persona. - La clasificación es por rango de RUT: cualquier RUT cuyo cuerpo (sin dígito verificador) sea igual o mayor a
50.000.000se trata como de persona jurídica. Es la misma regla que usa la verificación de identidad del resto de la plataforma; no consulta al SII. - Si tu sistema recibe el RUT de un formulario o de otro sistema, valida que corresponda a una persona antes de llamar a este endpoint, para no gastar un intento de creación con un RUT que nunca se va a poder verificar.
Ejemplo con curl
Sección titulada «Ejemplo con curl»curl -X POST "https://api.redcumbre.cl/{tenantSlug}/pin/transactions" \ -H "Authorization: Bearer {api_key}" \ -H "Content-Type: application/json" \ -d '{ "integration_slug": "mi_app_prod", "rut": "12.345.678-9", "authentication_method": "face", "operation_type": "firma_contrato", "operation_label": "Firmar Contrato N° 2026-042", "redirect_uri": "https://miapp.cl/pin-callback", "callback_url": "https://miapp.cl/webhooks/pin", "state": "session-abc-123" }'Respuesta Exitosa (201)
Sección titulada «Respuesta Exitosa (201)»{ "success": true, "data": { "transaction_id": "cedd740a-e5a3-43a2-9896-5ac45dfdb4dc", "authorize_url": "https://app.pinrut.cl/p/pin/authorize/7f3b9a61-2c4e-4d8f-b1a0-5e6d7c8b9a0f", "expires_at": "2026-05-18T23:30:04.594Z", "operation_hash": "06860b21052299ad..." }, "sandbox": false}| Campo | Tipo | Descripción |
|---|---|---|
transaction_id | string (UUID) | Identificador único de la transacción |
authorize_url | string (URL) | Dirección absoluta, en el dominio de PINRUT, a la que rediriges a la persona. Úsala tal cual: no le agregues nada delante. Contiene un identificador propio del enlace, distinto de transaction_id: no lo extraigas ni lo construyas |
expires_at | string (ISO 8601) | Límite absoluto de la transacción: la creación más 7 días y 48 horas, igual para toda transacción. Puede vencer antes (ver expiration_reason) |
operation_hash | string | Hash SHA-256 de la operación (para verificación de integridad) |
Redirect de la Persona
Sección titulada «Redirect de la Persona»Redirige al usuario a authorize_url tal como viene en la respuesta: ya es la dirección completa, en el dominio de
PINRUT. Por ejemplo:
https://app.pinrut.cl/p/pin/authorize/7f3b9a61-2c4e-4d8f-b1a0-5e6d7c8b9a0fQué pasa cuando la persona abre el enlace
Sección titulada «Qué pasa cuando la persona abre el enlace»La pantalla le muestra primero la operación y quién la pide, y después la lleva por lo que le falte:
- Con
piny PIN: ingresa su PIN. - Con
face, o conpiny sin PIN: se autentica con su cara. Conpin, después crea su PIN, y crearlo autoriza. - Sin identidad acreditada, en las dos modalidades: la acredita primero —consentimiento, cédula por ambos lados y prueba de vida— y vuelve al mismo punto.
Los plazos cortos corren desde que la persona abre el enlace, no desde que creas la transacción: 5 minutos para
ingresar el PIN y 30 minutos para la prueba de vida. La acreditación de identidad tiene el plazo de su sesión: hasta la
creación más 7 días y 48 horas. Si nadie abre el enlace en 7 días, la transacción vence con not_started.
La cédula no corresponde al RUT
Sección titulada «La cédula no corresponde al RUT»Al acreditar la identidad, la cédula que presenta la persona tiene que ser del RUT que declaraste al crear la transacción. Se compara apenas se lee la cédula, antes de la prueba de vida. Si es de otro RUT:
- la transacción pasa de inmediato a
failedconfailure_reason: "rut_mismatch", y recibespin.transaction.failed; - la persona ve que esa cédula no corresponde a quien debe autorizar la operación, y vuelve a tu sitio;
- no hay reintento dentro de la misma transacción. Si corresponde, abre una transacción nueva (por ejemplo, con el RUT corregido si lo habías declarado mal);
- no recibes ningún dato de la cédula presentada: ni el RUT que se leyó ni el nombre. Sólo el motivo.
Si la diferencia se descubre más tarde —por ejemplo, cuando una persona de nuestro equipo revisa la verificación—, la
transacción termina igual en rut_mismatch, con su webhook: nunca queda pendiente sin desenlace.
Consultar Resultado
Sección titulada «Consultar Resultado»Endpoint
Sección titulada «Endpoint»GET /{tenantSlug}/pin/transactions/{transaction_id}/resultHeaders
Sección titulada «Headers»Authorization: Bearer {api_key}Respuesta
Sección titulada «Respuesta»{ "success": true, "data": { "transaction_id": "cedd740a-e5a3-43a2-9896-5ac45dfdb4dc", "status": "authorized", "integration_slug": "mi_app_prod", "rut": "12.345.678-9", "operation_type": "firma_contrato", "operation_label": "Firmar Contrato N° 2026-042", "operation_reference": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "authentication_method": "face", "created_at": "2026-05-09T23:30:04.594Z", "authorized_at": "2026-05-09T23:33:01.538Z", "expires_at": "2026-05-18T23:30:04.594Z", "state": "session-abc-123", "expiration_reason": null, "failure_reason": null, "receipt": "eyJhbGciOiJFUzI1NiIsImtpZCI6Ii4uLiJ9.eyJ0cmFuc2FjY2lvbklkIjoiLi4uIn0.MEUCIQ...", "enrollment_charge": { "available": true, "price": 350, "currency": "CLP", "includesIva": false, "note": "Valores netos + IVA. Precio de lista del plan vigente, no una liquidación: la factura manda.", "desglose": [ { "metrica": "kyc_enrolamiento_iniciado", "price": 150 }, { "metrica": "kyc_enrolamiento_logrado", "price": 200 } ] }, "face_authentication_charge": { "available": false, "includesIva": false, "message": "El plan actual no tiene una tarifa unitaria resoluble para la autenticación facial. El cargo se aplica igual y el monto lo determina la factura." } }, "sandbox": false}El motivo (failure_reason)
Sección titulada «El motivo (failure_reason)»failure_reason viene siempre: es el mismo valor que el webhook pin.transaction.* de esa transacción entrega en su
failure_reason.
| Estado | failure_reason |
|---|---|
pending, authorized | null |
failed | "max_attempts_reached", "rate_limit_exceeded", "face_not_verified" o "rut_mismatch" |
expired | "transaction_expired" (el detalle va en expiration_reason) |
cancelled | "cancelled_by_integrator" o "cancelled_by_user" |
Así sabes por qué falló la transacción aunque no hayas recibido el webhook. rut_mismatch se explica en
La cédula no corresponde al RUT.
El comprobante
Sección titulada «El comprobante»receipt es el comprobante de la autorización: un JWS compacto que firma la transacción, el hash de la operación,
la modalidad, el instante de la autorización y la verificación que la respalda. Llega sólo con la transacción
authorized; en cualquier otro estado la clave no existe.
Se verifica sin relación con nosotros, contra las claves públicas de
/.well-known/kyc-jwks.json: toma el kid del encabezado del
JWS, busca esa clave en el JWKS y valida la firma con cualquier biblioteca JOSE. Guárdalo junto a tu operación: es la
prueba de que la persona la autorizó.
Si enviaste operation_reference al crear la transacción, el comprobante suma el claim operationReference con ese
mismo valor. Aparece sólo cuando lo mandaste: el comprobante de una transacción sin referencia es idéntico al de
siempre, sin la clave — un claim en null no atestiguaría nada.
Si tu integración recibe el nombre de la persona, el comprobante suma el claim nombreCompleto, firmado junto con lo
demás, y la respuesta trae full_name con el mismo valor.
El nombre de la persona (full_name)
Sección titulada «El nombre de la persona (full_name)»Sólo si tu integración tiene activado Nombre completo leído de la cédula al crear la transacción y al autorizarse (ver Recibir el nombre de la persona):
full_nameaparece sólo con la transacciónauthorized, con el nombre completo leído de la cédula, rectificado si la persona lo rectificó. En cualquier otro estado, o sin el dato activo, la clave no existe.- Es
nullcuando el comprobante ya no está: la persona fue anonimizada o la evidencia se retiró por antigüedad. - Sólo la credencial (API key) que creó la transacción puede consultarla: cualquier otra credencial de tu cuenta
recibe
404, igual que si la transacción no existiera.
El cargo de la acreditación de identidad
Sección titulada «El cargo de la acreditación de identidad»enrollment_charge informa el cargo de la acreditación de identidad que esta transacción gatilló.
- Es
nullcuando esa persona ya estaba acreditada y no hubo acreditación.nullsignifica no hubo cargo, no el cargo fue cero. - Cuando la tarifa del plan no se puede resolver, llega con
"available": falsey sumessage, sin monto. Nunca vas a recibir un cero por un cargo que sí ocurre. - El cargo de la transacción no se informa acá: está asumido en tu contrato y no cambia. El de la acreditación aparece a veces sí y a veces no —depende de si esa persona ya estaba acreditada—, y por eso es el único que te informamos por transacción.
El cargo de la autenticación facial
Sección titulada «El cargo de la autenticación facial»face_authentication_charge informa el cargo de la autenticación facial de esta transacción, con la misma forma que
enrollment_charge: las métricas pin_validacion_facial_iniciada y pin_validacion_facial_lograda que devengó, con su
precio de lista.
- Es
nullcuando la transacción no tuvo autenticación facial: la autorizó el PIN, o la acreditación de identidad. - Sin tarifa unitaria resoluble en tu plan llega con
"available": falsey sumessage, sin monto. Nunca un cero.
Las dos advertencias del monto son las mismas que para la acreditación: es precio de lista del plan vigente, no una liquidación —la factura manda—, y supone una tarifa unitaria plana.
El motivo del vencimiento
Sección titulada «El motivo del vencimiento»expiration_reason dice por qué venció una transacción:
| Valor | Cuándo |
|---|---|
"not_started" | La persona no empezó: nadie abrió el enlace en 7 días, o abrió una acreditación de identidad y no aceptó el consentimiento |
"not_completed" | La persona empezó y no terminó a tiempo: abrió el enlace y no ingresó el PIN, no completó la prueba de vida o no terminó la acreditación |
null | La transacción todavía no venció |
No existe un tercer valor: expiration_reason no te dice cómo terminó la acreditación ni la prueba de vida
—aprobada, rechazada o en revisión—, sólo si la persona empezó.
Cuando los datos de la transacción ya se anonimizaron
Sección titulada «Cuando los datos de la transacción ya se anonimizaron»La plataforma no conserva los datos personales de una transacción para siempre. Pasado su plazo, los anonimiza y
conserva el hecho: la transacción, su estado, su modalidad, su tipo, su etiqueta y sus fechas siguen respondiendo.
operation_hash no viene en esta consulta: lo recibes al crear la transacción y en sus webhooks, y tu copia es la que
sirve para verificar la integridad de la operación.
| Transacción | Cuándo | Qué cambia en la consulta |
|---|---|---|
No autorizada (expired, failed o cancelled) | 90 días después de su desenlace (cuando quedó expired, failed o cancelled) | rut, state y operation_reference llegan en null |
| Autorizada | 6 años después de authorized_at | receipt llega en null |
| De una persona cuyo registro venció | Al anonimizar el registro de la persona | rut, state, operation_reference y receipt llegan en null |
Guarda tu copia del comprobante cuando lo recibes. Tu copia sigue siendo verificable con el JWKS por su kid, y la
plataforma conserva la prueba de que lo emitió y cuándo aunque ya no lo entregue.
Cancelar Transacción
Sección titulada «Cancelar Transacción»Cancela una transacción pendiente antes de que la persona la complete:
Endpoint
Sección titulada «Endpoint»POST /{tenantSlug}/pin/transactions/{transaction_id}/cancelHeaders
Sección titulada «Headers»Authorization: Bearer {api_key}Respuesta
Sección titulada «Respuesta»{ "success": true, "data": { "success": true }, "sandbox": false}Solo transacciones en estado pending pueden cancelarse. Si la transacción ya está en un estado terminal, recibirás un error 409.
Estados de la Transacción
Sección titulada «Estados de la Transacción»| Estado | Terminal | Descripción |
|---|---|---|
pending | No | Persona aún no completó la verificación |
authorized | Sí | La persona se autenticó con la modalidad pedida — trae receipt |
failed | Sí | PIN incorrecto reiterado, PIN bloqueado, la verificación facial agotó sus intentos (face_not_verified), o la cédula presentada es de otro RUT (rut_mismatch) |
expired | Sí | La persona no completó a tiempo; expiration_reason dice si empezó o no |
cancelled | Sí | Cancelada por el integrador o por la persona |
Webhooks
Sección titulada «Webhooks»Recibirás webhooks cuando la transacción cambie a un estado terminal: en el callback_url de la transacción, siempre, o, si no lo enviaste, en la URL por defecto de tu empresa según su interruptor y su lista de eventos (ver Resolución del Webhook URL). Todos van firmados; cada entrega queda en el historial de la pestaña Configuración → Webhooks del panel de PINRUT.
Eventos
Sección titulada «Eventos»| Evento | Cuándo |
|---|---|
pin.transaction.authorized | La persona se autenticó con la modalidad pedida |
pin.transaction.failed | PIN incorrecto 5 veces, PIN bloqueado o verificación facial no completada |
pin.transaction.expired | La transacción venció sin completarse; expiration_reason dice si la persona empezó o no |
pin.transaction.cancelled | Cancelada por integrador o persona |
Payload del Webhook
Sección titulada «Payload del Webhook»{ "event": "pin.transaction.authorized", "timestamp": "2026-05-09T23:38:01.577Z", "data": { "transaction_id": "cedd740a-e5a3-43a2-9896-5ac45dfdb4dc", "integration_id": "702191e3-e415-41dc-ac1b-0b66d2edccf9", "rut": "12.345.678-9", "authentication_method": "pin", "operation_type": "firma_contrato", "operation_label": "Firmar Contrato N° 2026-042", "operation_hash": "06860b21052299ad...", "operation_reference": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "authorized_at": "2026-05-09T23:38:01.538Z", "status": "authorized", "state": "session-abc-123", "failure_reason": null, "expiration_reason": null, "receipt": "eyJhbGciOiJFUzI1NiIsImtpZCI6Ii4uLiJ9.eyJ0cmFuc2FjY2lvbklkIjoiLi4uIn0.MEUCIQ..." }}Si tu integración recibe el nombre de la persona, la autorización suma full_name al final de data, con el mismo
valor que el claim nombreCompleto del receipt. Ese webhook va sólo a la callback_url de la transacción:
{ "event": "pin.transaction.authorized", "data": { "transaction_id": "cedd740a-e5a3-43a2-9896-5ac45dfdb4dc", "status": "authorized", "receipt": "eyJhbGciOiJFUzI1NiIsImtpZCI6Ii4uLiJ9.eyJ0cmFuc2FjY2lvbklkIjoiLi4uIn0.MEUCIQ...", "full_name": "María José Pérez Soto" }}(Resumido: llegan también las demás claves del evento.)
Cuando vence una transacción cuya persona empezó y no terminó a tiempo:
{ "event": "pin.transaction.expired", "timestamp": "2026-05-12T10:14:02.101Z", "data": { "transaction_id": "cedd740a-e5a3-43a2-9896-5ac45dfdb4dc", "integration_id": "702191e3-e415-41dc-ac1b-0b66d2edccf9", "rut": "12.345.678-9", "authentication_method": "face", "operation_type": "firma_contrato", "operation_label": "Firmar Contrato N° 2026-042", "operation_hash": "06860b21052299ad...", "operation_reference": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "authorized_at": null, "status": "expired", "state": "session-abc-123", "failure_reason": "transaction_expired", "expiration_reason": "not_completed" }}Ningún evento lleva el identificador interno de la persona: es propio de la plataforma y la correlacionaría entre
integradores. Todos llevan authentication_method, y receipt va sólo en pin.transaction.authorized: en los
demás eventos la clave no existe. Lo mismo full_name, que además exige que tu integración reciba el nombre.
pin.transaction.expired llega a lo sumo una vez por transacción.
Campos Específicos por Evento
Sección titulada «Campos Específicos por Evento»| Evento | Campos adicionales en data |
|---|---|
pin.transaction.authorized | authorized_at, receipt |
pin.transaction.failed | failure_reason ("max_attempts_reached", "rate_limit_exceeded", "face_not_verified" o "rut_mismatch") |
pin.transaction.expired | failure_reason ("transaction_expired"), expiration_reason ("not_started" o "not_completed") |
pin.transaction.cancelled | — |
Resolución del Webhook URL
Sección titulada «Resolución del Webhook URL»El sistema determina dónde enviar el webhook con esta prioridad:
callback_urlenviado en la transacción. El webhook va sólo ahí, sin mirar la lista de eventos ni el interruptor «Enviar a esta URL»: declarar la URL ya es pedir el aviso.- URL por defecto de tu empresa, si la transacción no envió
callback_url. Va sólo si existe, si «Enviar a esta URL» está encendido y si Eventos habilitados deja pasar el evento (vacía = todos). - Ninguna de las dos → no se envía webhook (sólo redirect), y el historial lo muestra como «no enviada» con su motivo.
Los headers personalizados y la firma de tu configuración se aplican siempre, vaya el webhook al callback_url o a la URL por defecto.
Para no recibir los webhooks de una transacción en una URL propia, no declares callback_url. Para no recibirlos en la URL por defecto, quita los eventos pin.transaction.* de Eventos habilitados o apaga «Enviar a esta URL». El detalle está en A dónde va cada aviso.
Redirect
Sección titulada «Redirect»Cuando la persona completa la verificación (o cancela), el browser redirige a la redirect_uri con query parameters:
https://miapp.cl/pin-callback?transaction_id=cedd740a-...&status=authorized&state=session-abc-123| Parámetro | Descripción |
|---|---|
transaction_id | UUID de la transacción |
status | Estado final: authorized, failed o cancelled |
state | Valor opaco que enviaste al crear la transacción |
Códigos de Error
Sección titulada «Códigos de Error»| Código | Descripción | Causa |
|---|---|---|
| 400 | Validación fallida | Falta authentication_method o no es pin ni face, RUT inválido, redirect_uri no en lista permitida, callback_url no permitida, máximo de pendientes alcanzado, o el rut es de una persona jurídica (code: rut_de_persona_juridica; ver PINRUT es sólo para personas naturales) |
| 401 | No autorizado | API key inválida, expirada o revocada |
| 402 | Pago requerido | Tu plan no incluye la métrica de PINRUT (code: METRIC_NOT_IN_PLAN) |
| 403 | Prohibido | Servicio PIN_RUT no habilitado o rol insuficiente |
| 404 | No encontrado | Integración no existe o está inactiva, transacción no encontrada. También al consultar con otra credencial una transacción que entrega datos personales |
| 409 | Conflicto | Transacción no está en estado pending (al cancelar); o se envió un PIN a una transacción face (code: face_authentication_required) |
| 422 | No procesable | No hay redirect_uri y la integración no tiene default (code: no_redirect_uri); o la integración recibe datos personales y no enviaste callback_url (code: callback_url_required) |
| 423 | Bloqueado | Sólo con authentication_method: "pin": el PIN del RUT está bloqueado por demasiados intentos fallidos. Con face el PIN no interviene y no hay 423 |
| 503 | No disponible | El dominio de PINRUT no está disponible para la pantalla de autorización: no se creó la transacción y la respuesta no trae ninguna dirección. Reintenta más tarde |
Ejemplo de Error 404 (Integración)
Sección titulada «Ejemplo de Error 404 (Integración)»{ "message": "Integración activa con slug \"mi_app_prod\" no encontrada", "error": "Not Found", "statusCode": 404}Ejemplo de Error 400 (RUT de Persona Jurídica)
Sección titulada «Ejemplo de Error 400 (RUT de Persona Jurídica)»{ "code": "rut_de_persona_juridica", "message": "PINRUT es sólo para personas naturales: el RUT ingresado corresponde a una persona jurídica (una empresa). Usa el RUT personal del titular, el de su cédula de identidad."}Ejemplo de Error 423 (RUT Bloqueado)
Sección titulada «Ejemplo de Error 423 (RUT Bloqueado)»{ "code": "pin_blocked", "message": "RUT is blocked due to too many failed attempts", "statusCode": 423}Protección Anti-Brute-Force
Sección titulada «Protección Anti-Brute-Force»El sistema implementa un rate limit global de intentos fallidos del PIN:
- Máximo 10 intentos fallidos de PIN por RUT en 1 hora (cross-tenant). Las verificaciones faciales fallidas no cuentan: no bloquean el PIN
- Al superar el límite, el PIN del RUT se bloquea inmediatamente
- Transacciones nuevas para un RUT bloqueado retornan 423 Locked
- El bloqueo es global: protege contra ataques distribuidos desde múltiples integraciones
Límites
Sección titulada «Límites»| Concepto | Límite |
|---|---|
| Transacciones pendientes por RUT × integración | 3 |
| Intentos de PIN por transacción | 5 |
| Intentos de verificación facial por transacción | 3 |
| Intentos fallidos globales de PIN por RUT/hora | 10 |
| Plazo para abrir el enlace | 7 días desde la creación |
| Plazo para ingresar el PIN, desde que se abre el enlace | 5 minutos |
| Plazo para la prueba de vida, desde que se abre el enlace | 30 minutos |
| Plazo de una acreditación de identidad | Hasta la creación más 7 días y 48 horas (expires_at) |
Tamaño máximo de state | 2048 caracteres |
Tamaño máximo de operation_label | 200 caracteres |
Tamaño máximo de operation_detail | 500 caracteres |
Formato y tamaño de operation_reference | sha256:<64 hex en minúsculas>, 71 caracteres exactos |
Integración JavaScript
Sección titulada «Integración JavaScript»// 1. Crear transacciónconst response = await fetch( `https://api.redcumbre.cl/${tenantSlug}/pin/transactions`, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ integration_slug: 'mi_app_prod', rut: '12.345.678-9', operation_type: 'firma_contrato', operation_label: 'Firmar Contrato N° 2026-042', redirect_uri: 'https://miapp.cl/pin-callback', callback_url: 'https://miapp.cl/webhooks/pin', state: sessionId, }), });
const { data } = await response.json();
// 2. Redirigir al usuariowindow.location.href = data.authorize_url; // ya es la dirección completa
// 3. En la página de callback, verificar resultadoconst params = new URLSearchParams(window.location.search);const txId = params.get('transaction_id');const status = params.get('status');
if (status === 'authorized') { // Verificar server-side antes de tomar acción const result = await fetch( `https://api.redcumbre.cl/${tenantSlug}/pin/transactions/${txId}/result`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); const { data: tx } = await result.json();
if (tx.status === 'authorized') { // Identidad verificada — proceder con la operación }}Receptor de Webhook (Node.js)
Sección titulada «Receptor de Webhook (Node.js)»app.post('/webhooks/pin', (req, res) => { const { event, data } = req.body;
switch (event) { case 'pin.transaction.authorized': console.log(`✓ Identidad verificada: RUT=${data.rut}, tx=${data.transaction_id}`); // Procesar autorización break;
case 'pin.transaction.failed': console.log(`✗ Verificación fallida: ${data.failure_reason}`); break;
case 'pin.transaction.expired': console.log(`⏰ Transacción expirada: ${data.transaction_id}`); break;
case 'pin.transaction.cancelled': console.log(`↩ Transacción cancelada: ${data.transaction_id}`); break; }
res.json({ received: true });});