Inicio rápido
sf_test_…).sf_live_…: cada firma pagada se descuenta de su saldo.curl https://azur.com.ec/plataforma/api/firmas/v1/cuenta -H "Authorization: Bearer sf_test_SU_CREDENCIAL" curl https://azur.com.ec/plataforma/api/firmas/v1/catalogo -H "Authorization: Bearer sf_test_SU_CREDENCIAL"
URL base: https://azur.com.ec/plataforma/api/firmas/v1. Todas las respuestas tienen la forma {"ok": true, "datos": …} o {"ok": false, "error": {"codigo", "mensaje", "detalle", "reintentable"}}.
Autenticación
Envíe su credencial en la cabecera Authorization: Bearer sf_live_… (o X-Api-Key). Nunca en la URL ni en el cuerpo. Desde su panel puede:
- tener varias credenciales con nombre (una por sistema) y revocar cualquiera al instante;
- restringir cada una a sus IP (o rangos CIDR) y ponerle fecha de vencimiento.
Solo vemos un resumen (hash) de la credencial: si la pierde, revóquela y cree otra.
Pruebas y producción
| Credencial | Qué pasa |
|---|---|
sf_test_… | Valida exactamente igual que producción (datos, documentos, vigencias), pero no crea firmas reales, no descuenta saldo y nada llega a la entidad certificadora. Puede simular estados con POST /firmas/{id}/simular. |
sf_live_… | Firmas reales. Con accion: "pagar" se descuenta de su saldo prepago (IVA incluido) y la solicitud entra a revisión de documentos. |
Flujo de una firma
En cualquier punto de la revisión puede pasar a requiere_correccion (nuestro equipo o la entidad certificadora piden corregir un dato o un documento): usted corrige por la API o en su panel y vuelve a ingresada.
Cuando queda emitida, el titular recibe su archivo .p12 y su clave en su correo.
Tipos de firma y documentos requeridos
| tipo | Para quién | Documentos obligatorios |
|---|---|---|
persona_natural | Una persona, con o sin RUC | cedula_frontal, cedula_posterior, selfie; si envía titular.ruc, además copia_ruc. Opcional: adicional. |
persona_juridica | El representante legal de una empresa | Los de cédula y selfie + copia_ruc, constitucion, nombramiento. Opcional: adicional. |
miembro_empresa | Un empleado autorizado por el representante legal | Los de cédula y selfie + copia_ruc, nombramiento (del representante), cedula_representante, autorizacion_representante. Opcional: constitucion. |
| Documento | Qué es |
|---|---|
cedula_frontal | Cédula del titular: parte frontal (foto o PDF legible). |
cedula_posterior | Cédula del titular: parte posterior. |
selfie | Foto selfie del titular (rostro visible, sin lentes oscuros ni gorra). |
copia_ruc | Copia del RUC (PDF del SRI). |
constitucion | Escritura de constitución de la empresa. |
nombramiento | Nombramiento del representante legal vigente. |
cedula_representante | Cédula del representante legal. |
autorizacion_representante | Autorización firmada por el representante legal para emitir la firma al miembro. |
adicional | Documento adicional (opcional). |
Formatos: JPG, PNG o PDF, hasta 8 MB cada uno. Revisamos el contenido real del archivo, no su extensión. Las vigencias disponibles y su precio para usted (según su nivel del mes) están en GET /catalogo.
Crear una firma
POST /firmas con la cabecera Idempotency-Key. Elija la acción:
"accion": "pagar"— se descuenta de su saldo y entra a revisión de inmediato. Si el saldo no alcanza responde402 saldo_insuficientey no crea nada."accion": "borrador"— se guarda sin cobrar (útil si su cliente aún no paga). No la revisamos hasta que la pague; vence a los 7 días.
curl -X POST https://azur.com.ec/plataforma/api/firmas/v1/firmas \
-H "Authorization: Bearer sf_live_SU_CREDENCIAL" \
-H "Idempotency-Key: 6f1c2b7e-1d1a-4c9e-9a51-2f0d8b4c1a10" \
-H "Content-Type: application/json" \
-d '{
"tipo": "persona_natural",
"accion": "pagar",
"vigencia": "1 año",
"referencia_externa": "PEDIDO-1001",
"titular": {
"tipo_identificacion": "CEDULA", "identificacion": "0912345678", "codigo_dactilar": "V4443V4444",
"nombres": "MARIA JOSE", "primer_apellido": "PEREZ", "segundo_apellido": "LOPEZ",
"fecha_nacimiento": "1990-05-20", "nacionalidad": "ECUATORIANA", "sexo": "MUJER",
"celular": "0991234567", "email": "maria.perez@ejemplo.com"
},
"domicilio": { "provincia": 9, "ciudad": 1, "direccion": "AV. 9 DE OCTUBRE 100 Y MALECON" },
"documentos": {
"cedula_frontal": "data:image/jpeg;base64,/9j/4AAQ...",
"cedula_posterior": "data:image/jpeg;base64,/9j/4AAQ...",
"selfie": "data:image/jpeg;base64,/9j/4AAQ..."
}
}'
Respuesta 201 con la firma (id, estado, total descontado, documentos recibidos…). Guarde el id: con él consulta, paga, corrige y lo recibe en los webhooks.
Para persona_juridica y miembro_empresa agregue el bloque empresa (ruc, razon_social, cargo y, para miembro, area) y, para miembro, representante_legal. Ejemplos completos en la referencia.
Si hay errores le devolvemos todos juntos en error.detalle con el nombre del campo (titular.celular, documentos.selfie…), para que los corrija de una vez.
Enviar los documentos
Opción 1: JSON con base64
Como en el ejemplo anterior: cada documento como data URI (data:image/jpeg;base64,…) o el base64 solo.
Opción 2: multipart (archivos)
curl -X POST https://azur.com.ec/plataforma/api/firmas/v1/firmas \
-H "Authorization: Bearer sf_live_SU_CREDENCIAL" \
-H "Idempotency-Key: 0b8e5f62-2b0e-4d8f-8c3e-1a2b3c4d5e6f" \
-F 'datos={"tipo":"persona_natural","accion":"borrador","vigencia":"1 año","titular":{...},"domicilio":{...}}' \
-F "documentos[cedula_frontal]=@frontal.jpg" \
-F "documentos[cedula_posterior]=@posterior.jpg" \
-F "documentos[selfie]=@selfie.jpg"
Pagar un borrador
curl -X POST https://azur.com.ec/plataforma/api/firmas/v1/firmas/123456/pagar \ -H "Authorization: Bearer sf_live_SU_CREDENCIAL" \ -H "Idempotency-Key: 9a7c...-pago-123456"
Se cobra al precio con el que se guardó. Para eliminar un borrador que ya no va: DELETE /firmas/{id} (solo borradores sin pagar).
Consultar y listar
GET https://azur.com.ec/plataforma/api/firmas/v1/firmas/123456 GET https://azur.com.ec/plataforma/api/firmas/v1/firmas?estado=requiere_correccion&desde=2026-10-01&pagina=1 GET https://azur.com.ec/plataforma/api/firmas/v1/firmas?referencia=PEDIDO-1001
El listado incluye también las firmas que creó en su panel web. 50 por página.
Correcciones: por la API o en su panel, usted decide
Cuando una firma pasa a requiere_correccion, en correccion le decimos el motivo y qué campos corregir:
"correccion": { "motivo": "La selfie no muestra el rostro completo.", "campos": ["documentos.selfie"] }
Corríjala con POST /firmas/{id}/correccion enviando solo lo que cambia (los documentos que ya tiene no hace falta reenviarlos), o ábrala en su panel web como siempre. También puede corregir una firma ingresada antes de que la revisemos.
curl -X POST https://azur.com.ec/plataforma/api/firmas/v1/firmas/123456/correccion \
-H "Authorization: Bearer sf_live_SU_CREDENCIAL" \
-H "Idempotency-Key: 3c2d...-correccion-1" \
-H "Content-Type: application/json" \
-d '{ "documentos": { "selfie": "data:image/jpeg;base64,/9j/4AAQ..." } }'
El tipo, el formato y la vigencia no se cambian por aquí.
Estados
| estado | Significado |
|---|---|
borrador | Guardada sin pagar. No la revisamos hasta que se pague; vence a los 7 días de creada. |
ingresada | Pagada con su saldo y en cola de revisión de documentos. |
verificada | Documentos revisados; lista para enviarse a la entidad certificadora. |
en_tramite | En validación por la entidad certificadora. |
aprobada | Aprobada por la entidad certificadora; falta que el titular la descargue/emita. |
emitida | Firma emitida. El titular recibe el archivo .p12 y su clave en su correo. |
requiere_correccion | Hay que corregir datos o documentos (ver «correccion»). Corríjala por la API o en el panel. |
eliminada | Dada de baja (borrador vencido o eliminado). |
suspendida | Certificado suspendido. |
revocada | Certificado revocado. |
en_revocacion | Revocación en curso. |
cancelada | Solicitud cancelada. |
Webhooks
Regístrelos en su panel o con POST /webhooks (url https pública y, si quiere, la lista de eventos). Le enviamos un POST JSON cada vez que una firma cambia:
| Evento | Cuándo |
|---|---|
firma.borrador | Se creó como borrador (sin pagar). |
firma.ingresada | Se pagó con su saldo y entró a revisión (al crearla o al pagar el borrador). |
firma.verificada | Nuestro equipo revisó los documentos. |
firma.en_tramite | Se envió a la entidad certificadora. |
firma.aprobada | La entidad certificadora la aprobó. |
firma.emitida | Firma emitida: el titular recibió su archivo .p12. |
firma.requiere_correccion | Hay que corregir datos o documentos (vea «correccion»). |
firma.eliminada | Dada de baja (p. ej. borrador vencido a los 7 días). |
firma.cancelada | Solicitud cancelada. |
firma.suspendida | Certificado suspendido. |
firma.revocada | Certificado revocado. |
firma.en_revocacion | Revocación en curso. |
webhook.prueba | Envío de prueba pedido desde la API o el panel. |
{
"id_evento": "b3f2c1e0-…",
"evento": "firma.emitida",
"momento": "2026-10-17T15:42:10-05:00",
"ambiente": "produccion",
"datos": { "id": 123456, "referencia_externa": "PEDIDO-1001", "estado": "emitida", … }
}
Verifique la firma de cada aviso
Cabeceras: X-Webhook-Evento, X-Webhook-Id (único: úselo para no procesar dos veces), X-Webhook-Momento y X-Webhook-Firma = HMAC-SHA256 en hexadecimal de "{momento}.{cuerpo}" con el secreto que le dimos al crear el webhook.
// PHP
$cuerpo = file_get_contents('php://input');
$esperada = hash_hmac('sha256', $_SERVER['HTTP_X_WEBHOOK_MOMENTO'] . '.' . $cuerpo, $secreto);
if (!hash_equals($esperada, $_SERVER['HTTP_X_WEBHOOK_FIRMA'])) { http_response_code(401); exit; }
$evento = json_decode($cuerpo, true); // responda 200 rápido y procese después
// Node.js (Express con express.raw())
const esperada = crypto.createHmac('sha256', secreto).update(req.get('X-Webhook-Momento') + '.' + req.body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(esperada), Buffer.from(req.get('X-Webhook-Firma')))) return res.sendStatus(401);
Responda 2xx en menos de 10 segundos. Si no, reintentamos a los 1, 5, 15 y 60 minutos y a las 3, 6 y 12 horas. Puede ver cada envío en su panel o con GET /webhooks/{id}/envios, y mandar uno de prueba con POST /webhooks/{id}/probar.
Idempotencia: nunca se cobra dos veces
Crear, pagar, corregir y eliminar exigen la cabecera Idempotency-Key (un UUID nuevo por operación). Si se corta la red y reintenta con la misma llave, le devolvemos la misma respuesta sin volver a crear ni cobrar (cabecera Idempotent-Replayed: true).
La misma llave con otros datos responde 422 idempotencia_distinta. Además, referencia_externa no se puede repetir: es una segunda red de seguridad con su propio número de pedido.
Errores
| HTTP | codigo | Qué hacer |
|---|---|---|
| 401 | credencial_invalida | Revise la credencial (revocada o vencida). |
| 403 | sin_permiso | IP no permitida para esa credencial, o acción solo de pruebas. |
| 402 | saldo_insuficiente | Recargue saldo o guárdela como borrador. |
| 409 | duplicada | Ya hay una firma en trámite para esa identificación, el celular ya tiene 3 firmas vigentes o la referencia ya existe. |
| 409 | estado_no_permite | La acción no aplica al estado actual (p. ej. pagar algo que no es borrador). |
| 422 | datos_invalidos / documentos_faltantes | Corrija los campos de error.detalle. |
| 428 | falta_idempotencia | Envíe Idempotency-Key. |
| 429 | demasiadas_peticiones | Espere y reintente. |
| 500 | error_interno | Reintente con la misma Idempotency-Key. |
Límites y buenas prácticas
- 120 llamadas por minuto por credencial. 50 resultados por página.
- Hasta 20 borradores abiertos a la vez; vencen a los 7 días de creados.
- Solo firmas en archivo (.p12).
- Use los webhooks en lugar de consultar cada pocos segundos.
- Guarde la credencial en su servidor, nunca en el navegador ni en una app móvil.
¿Dudas? Escríbanos desde su panel. ¿Listo para empezar? Cree su credencial de pruebas.