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

  1. Pide tu clave API al administrador (empieza con gk_live_).
  2. Crea una verificación con POST /v1/verifications.
  3. Redirige a tu usuario a la url de la respuesta.
  4. 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.

Nunca pongas la clave en código del navegador o de una app móvil. Llama a la API desde tu backend. Si se filtra, pide que la revoquen y te den otra.

Rutas

POST/v1/verifications

Crea una verificación y devuelve la URL donde el usuario la completa.

CampoTipoDescripción
verification_typetexto, requeridoCódigo del tipo (ver /v1/verification-types).
external_reftexto, opcionalTu identificador del usuario u orden (máx. 200 caracteres). Se devuelve tal cual.
redirect_urlURL https, opcionalA 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ámetroDescripción
limit1 a 100 (por defecto 20).
statusFiltra por estado.
created_beforeFecha 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

EstadoSignificado¿Se cobra?
creadaSesión creada, el usuario aún no empieza.No
en_procesoEl usuario está verificándose.No
approvedVerificación aprobada.Sí
declinedVerificación rechazada.Sí
in_reviewEn revisión manual.Sí
abandonadaEl 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.

EventoCuándo
verification.completedLa verificación llegó a un resultado final cobrable.
verification.updatedUna 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": "..." } }.

HTTPcodeCausa
401invalid_api_keyClave inválida o revocada.
402insufficient_balanceSaldo insuficiente (incluye las verificaciones en curso).
403type_not_in_planTu plan no incluye ese tipo.
403account_inactiveCuenta inactiva.
404unknown_verification_typeEl tipo no existe.
404not_foundRuta o verificación inexistente.
409no_priceLa cuenta aún no tiene precio asignado.
409no_planLa cuenta aún no tiene un plan asignado, así que no puede verificar.
409type_unavailableEse tipo está en tu plan, pero no está disponible con tu precio actual. Pide al administrador que lo revise.
422invalid_requestFalta verification_type o el cuerpo es inválido.
422invalid_redirect_urlredirect_url no es una URL https válida.
422redirect_domain_not_allowedEl dominio no está entre tus dominios verificados.
502provider_unavailableNo se pudo iniciar la sesión. Reintenta; no se cobra.
503service_not_configuredEl servicio aún no está configurado para ese tipo.
500internal_errorError 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