API de Guardián KYC
Verifica la identidad de tus usuarios desde cualquier app, web o programa. Creas una verificación, envías al usuario a la URL que recibes y te avisamos el resultado.
Inicio rápido
URL base: https://psldbnrvhjoajhepiwdj.supabase.co/functions/v1/api
- Pide tu clave API al administrador (empieza con
gk_live_). - Crea una verificación con
POST /v1/verifications. - Redirige a tu usuario a la
urlde la respuesta. - Recibe el resultado en tu webhook o consulta
GET /v1/verifications/:id.
curl -X POST "$BASE/v1/verifications" \
-H "Authorization: Bearer gk_live_TU_CLAVE" \
-H "Content-Type: application/json" \
-d '{"verification_type":"facial","external_ref":"usuario-123","redirect_url":"https://tuapp.com/listo"}'
Autenticación
Todas las peticiones llevan la cabecera Authorization: Bearer gk_live_.... La clave se muestra completa una sola vez al crearla; guárdala en tu servidor.
Rutas
POST/v1/verifications
Crea una verificación y devuelve la URL donde el usuario la completa.
| Campo | Tipo | Descripción |
|---|---|---|
verification_type | texto, requerido | Código del tipo (ver /v1/verification-types). |
external_ref | texto, opcional | Tu identificador del usuario u orden (máx. 200 caracteres). Se devuelve tal cual. |
redirect_url | URL https, opcional | A dónde regresa el usuario al terminar. Si tienes dominios verificados, debe pertenecer a ellos. |
201 Created
{
"id": "0b6f2c1e-8d1a-4c5e-9a53-2f6a1c7d9e10",
"status": "creada",
"url": "https://...",
"verification_type": "facial",
"external_ref": "usuario-123",
"created_at": "2026-10-05T14:02:11.000Z"
}
Antes de crear la sesión se comprueba que tu plan incluya el tipo y que tengas saldo para esta verificación y las que están en curso.
GET/v1/verifications/:id
Devuelve una verificación tuya.
{
"id": "0b6f2c1e-...",
"status": "approved",
"verification_type": "facial",
"external_ref": "usuario-123",
"charged_amount": 1.05,
"created_at": "2026-10-05T14:02:11.000Z",
"completed_at": "2026-10-05T14:03:40.000Z"
}
GET/v1/verifications
Lista tus verificaciones, de la más nueva a la más antigua.
| Parámetro | Descripción |
|---|---|
limit | 1 a 100 (por defecto 20). |
status | Filtra por estado. |
created_before | Fecha ISO 8601; devuelve las anteriores. Úsalo para paginar con el created_at del último resultado. |
{ "data": [ { "id": "...", "status": "approved", ... } ] }
GET/v1/balance
{
"balance": 42.00,
"price_per_verification": 1.05,
"verifications_available": 40,
"currency": "USD"
}
GET/v1/verification-types
Tipos de verificación disponibles en tu plan.
{ "data": [ { "code": "facial", "name": "...", "description": "..." } ] }
Estados y cobro
| Estado | Significado | ¿Se cobra? |
|---|---|---|
creada | Sesión creada, el usuario aún no empieza. | No |
en_proceso | El usuario está verificándose. | No |
approved | Verificación aprobada. | Sí |
declined | Verificación rechazada. | Sí |
in_review | En revisión manual. | Sí |
abandonada | El usuario no terminó, o expiró. | No |
Se cobra una sola vez por verificación, aunque recibas el aviso más de una vez. Si una verificación in_review pasa después a approved o declined, cambia el estado sin cobrar de nuevo.
Webhooks
Te enviamos un POST con JSON a tu URL registrada cuando una verificación termina. Responde con código 2xx en menos de 8 segundos.
| Evento | Cuándo |
|---|---|
verification.completed | La verificación llegó a un resultado final cobrable. |
verification.updated | Una revisión manual terminó en aprobada o rechazada. |
{
"id": "evento-uuid",
"type": "verification.completed",
"created": "2026-10-05T14:03:41.000Z",
"data": {
"verification_id": "0b6f2c1e-...",
"status": "approved",
"verification_type": "facial",
"external_ref": "usuario-123",
"charged_amount": 1.05
}
}
Verificar la firma
La cabecera X-Guardian-Signature tiene la forma t=TIMESTAMP,v1=FIRMA. La firma es HMAC-SHA256, en hexadecimal, de TIMESTAMP + "." + cuerpo_crudo con tu secreto de webhook. Rechaza peticiones con un t de hace más de 5 minutos.
// Node.js
import crypto from "node:crypto";
function firmaValida(cuerpoCrudo, cabecera, secreto) {
const p = Object.fromEntries(cabecera.split(",").map(s => s.split("=")));
if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
const esperada = crypto.createHmac("sha256", secreto)
.update(p.t + "." + cuerpoCrudo).digest("hex");
return crypto.timingSafeEqual(Buffer.from(esperada), Buffer.from(p.v1));
}
Usa el cuerpo tal como llegó, sin volver a serializarlo. Registra tu URL y copia tu secreto en la pestaña Integración de tu panel de cliente.
Errores
Los errores devuelven { "error": { "code": "...", "message": "..." } }.
| HTTP | code | Causa |
|---|---|---|
| 401 | invalid_api_key | Clave inválida o revocada. |
| 402 | insufficient_balance | Saldo insuficiente (incluye las verificaciones en curso). |
| 403 | type_not_in_plan | Tu plan no incluye ese tipo. |
| 403 | account_inactive | Cuenta inactiva. |
| 404 | unknown_verification_type | El tipo no existe. |
| 404 | not_found | Ruta o verificación inexistente. |
| 409 | no_price | La cuenta aún no tiene precio asignado. |
| 409 | no_plan | La cuenta aún no tiene un plan asignado, así que no puede verificar. |
| 409 | type_unavailable | Ese tipo está en tu plan, pero no está disponible con tu precio actual. Pide al administrador que lo revise. |
| 422 | invalid_request | Falta verification_type o el cuerpo es inválido. |
| 422 | invalid_redirect_url | redirect_url no es una URL https válida. |
| 422 | redirect_domain_not_allowed | El dominio no está entre tus dominios verificados. |
| 502 | provider_unavailable | No se pudo iniciar la sesión. Reintenta; no se cobra. |
| 503 | service_not_configured | El servicio aún no está configurado para ese tipo. |
| 500 | internal_error | Error interno. Reintenta. |
Ejemplo completo (Node.js)
const BASE = "https://psldbnrvhjoajhepiwdj.supabase.co/functions/v1/api";
const KEY = process.env.GUARDIAN_KEY; // gk_live_...
const r = await fetch(BASE + "/v1/verifications", {
method: "POST",
headers: { Authorization: "Bearer " + KEY, "Content-Type": "application/json" },
body: JSON.stringify({ verification_type: "facial", external_ref: "usuario-123" })
});
const v = await r.json();
if (!r.ok) throw new Error(v.error.code);
// Envía al usuario a v.url