Integración entre aplicaciones
Cómo se identifica una aplicación que consume esta API, qué puede hacer, cómo se configura sin intervención humana y cómo se reporta un error. Toda la gestión está disponible por REST y por MCP con las mismas reglas.
Identidad: usuario o aplicación
Toda petición lleva un token Bearer que se resuelve a un principal. Hay dos tipos, y se distinguen en la autorización y en la auditoría:
| Principal | Sujeto de autorización | Actor en la bitácora de timbrado | Cuándo |
|---|---|---|---|
| Usuario (key personal) | <correo> | uid:<id> | Una persona automatiza algo propio. |
| Aplicación (key de máquina) | app:<cuenta>:<slug> | app:<cuenta>:<slug> | Otra aplicación se integra (ERP, portales, ad_cfdi…). |
Toda aplicación tiene un usuario responsable: si su cuenta se suspende, o si la aplicación se desactiva en /aplicaciones, sus keys dejan de autenticar de inmediato (401).
Autorización en dos capas
El alcance efectivo de una petición es la intersección del permiso del sujeto y de los scopes del token. Falta cualquiera de los dos y la respuesta es 403.
- Permiso del sujeto (Casbin): lo da el rol —
admin,usuario(todo humano con cuenta) oaplicacion—. - Scopes del token: se eligen al crear la API key y no se pueden ampliar después; para dar más alcance se crea otra key.
El acceso a los emisores tampoco lo da ninguna de las dos capas: lo da la cuenta maestra (tenant) del principal, y cada consulta filtra por cuenta_id. Un RFC es único dentro de su cuenta y puede repetirse en otras, donde es un sello independiente e invisible. Un identificador enviado por el cliente nunca autoriza por sí solo. Las cuentas maestras se gestionan en /cuentas.
Scopes disponibles
| Scope | Qué permite |
|---|---|
catalogos:read | Consultar y descargar los catálogos de claves del SAT |
actualizaciones:read | Consultar el estado de versiones de los catálogos |
cfdi:validar | Validar CFDI 4.0 (esquema, catálogos y reglas de negocio) |
cfdi:generar | Generar y sellar CFDI (y timbrarlos con ?timbrar=true) |
cfdi:consulta | Consultar el estado de un CFDI timbrado ante el SAT |
documentos:read | Listar y descargar los documentos CFDI guardados (XML y PDF) |
empresas:read | Listar los emisores (empresas) a los que se tiene acceso |
empresas:write | Dar de alta emisores con su CSD y editar régimen fiscal, liga con el agente y accesos |
aplicaciones:read | Listar las aplicaciones registradas |
aplicaciones:write | Registrar y configurar aplicaciones (reservado a administradores) |
apikeys:manage | Crear y revocar API keys dentro de los propios scopes |
cuentas:read | Consultar cuentas maestras y su uso (reservado a administradores de plataforma) |
cuentas:write | Crear y suspender cuentas maestras (reservado a administradores de plataforma) |
Endpoints de gestión
| Método y ruta | Scope | Permiso | Descripción |
|---|---|---|---|
GET /api/v1/empresas | empresas:read | empresa:read | Emisores a los que tiene acceso el token, con su rol de acceso. |
GET /api/v1/empresas/:rfc | empresas:read | empresa:read | Metadatos de un emisor. |
POST /api/v1/empresas | empresas:write | empresa:write | Alta de un emisor con su CSD en base64; concede administrar al que la crea. |
PATCH /api/v1/empresas/:rfc | empresas:write | empresa:write | Edita regimen_fiscal y/o agente_empresa (rol administrar). |
DELETE /api/v1/empresas/:rfc | empresas:write | empresa:write | Borra el emisor y sus accesos (rol administrar). |
GET /api/v1/empresas/:rfc/agente/prueba | empresas:read | empresa:read | Verifica la liga con el agente de timbrado de la cuenta. |
GET /api/v1/aplicaciones | aplicaciones:read | aplicacion:read | Aplicaciones registradas (administradores). |
GET /api/v1/aplicaciones/:slug | aplicaciones:read | aplicacion:read | Detalle de una aplicación con sus API keys. |
POST /api/v1/aplicaciones | aplicaciones:write | aplicacion:write | Registra una aplicación (administradores). |
PATCH /api/v1/aplicaciones/:slug | aplicaciones:write | aplicacion:write | Edita el nombre o activa/desactiva la aplicación. |
POST /api/v1/aplicaciones/:slug/keys | apikeys:manage | apikey:manage | Crea una API key de la aplicación y devuelve el token una sola vez. |
GET /api/v1/api-keys | apikeys:manage | apikey:manage | API keys del principal que llama. |
POST /api/v1/api-keys | apikeys:manage | apikey:manage | Crea una key propia (personal o de su aplicación). |
POST /api/v1/api-keys/:id/revocar | apikeys:manage | apikey:manage | Desactiva una key propia. |
GET /api/v1/cuenta/uso | cuentas:read | cuenta:read | Meterización de la cuenta del token (por métrica y mes). |
GET /api/v1/cuentas | cuentas:write | cuenta:write | Cuentas maestras registradas (plataforma). |
POST /api/v1/cuentas | cuentas:write | cuenta:write | Registra una cuenta maestra (plataforma). |
PATCH /api/v1/cuentas/:id | cuentas:write | cuenta:write | Edita nombre/estado; suspenderla corta a sus usuarios y keys. |
Bootstrap de una integración
Con una key de administrador (scopes aplicaciones:write, apikeys:manage y empresas:write), el alta de una aplicación consumidora no requiere tocar el panel:
# 1. Registrar la aplicación
curl -sX POST $BASE/api/v1/aplicaciones \
-H "Authorization: Bearer $ADMIN_KEY" -H 'Content-Type: application/json' \
-d '{"nombre":"AD CFDI","slug":"ad-cfdi"}'
# 2. Crear su API key con los scopes mínimos (el token se muestra una sola vez)
curl -sX POST $BASE/api/v1/aplicaciones/ad-cfdi/keys \
-H "Authorization: Bearer $ADMIN_KEY" -H 'Content-Type: application/json' \
-d '{"nombre":"prod","scopes":["cfdi:generar","empresas:read"],"expira_en":"2027-01-01T00:00:00Z"}'
# 3. Dar de alta el emisor con el CSD en base64
curl -sX POST $BASE/api/v1/empresas \
-H "Authorization: Bearer $APP_KEY" -H 'Content-Type: application/json' \
-d "{\"cer_b64\":\"$(base64 -i sello.cer)\",\"key_b64\":\"$(base64 -i sello.key)\",
\"password\":\"$PASS_CSD\",\"regimen_fiscal\":\"601\"}"
# 4. Generar (y timbrar) con esa key
curl -sX POST "$BASE/api/v1/cfdi/generar/ingreso?timbrar=true" \
-H "Authorization: Bearer $APP_KEY" -H 'Content-Type: application/json' -d @factura.jsonSi el emisor ya existe y lo administra otro principal, el paso 3 es una concesión desde quien administra: POST /api/v1/empresas/<RFC>/accesos con {"aplicacion_slug":"ad-cfdi","rol":"emitir"}.
Reglas que conviene conocer
- Un sello por RFC y cuenta. Si el RFC ya está registrado en la cuenta, el alta responde 409
rfc_duplicado; en otra cuenta se acepta porque es un sello independiente. Rotar un CSD es editar el sello existente. - Anti-escalada. Con una key solo se crean keys con scopes que esa key ya tiene, y una aplicación solo crea keys de sí misma (403
escalada_de_scopes). - El token se muestra una sola vez. Ni la API ni el panel permiten volver a consultarlo; si se pierde, se crea otra key y se revoca la anterior.
- 404 opaco. Un emisor que existe pero al que el token no tiene acceso responde 404
empresa_no_encontrada, igual que si no existiera. - Auditoría y meterización. Cada timbrado guarda en
timbrado_operacionla cuenta, el actor (uid:<id>oapp:<cuenta>:<slug>) y la aplicación, y el agente recibe ese actor comousuario_plataforma. El consumo por cuenta se acumula encuenta_usoy se consulta enGET /api/v1/cuenta/uso. - Licencia. Una cuenta suspendida responde 403
cuenta_suspendiday una sin licencia vigente 402licencia_vencida; la vigencia la decide el sistema de licenciamiento.
Contrato de errores
Toda la API (/api/v1/* y /mcp) responde el mismo envelope:
{
"error": {
"codigo": "empresa_no_encontrada",
"mensaje": "No tiene acceso a una empresa con ese RFC.",
"detalles": []
}
}codigo es estable y es lo que debe leer un consumidor; mensaje está listo para mostrarse y detalles lleva los scopes faltantes, los campos rechazados o el código del proveedor. La especificación de máquina está en /docs/openapi.yaml.
| Código | HTTP | Significado |
|---|---|---|
token_ausente | 401 | Falta el encabezado Authorization: Bearer <token>. |
token_invalido | 401 | El token no existe, fue revocado, expiró, o su usuario responsable está suspendido o su aplicación inactiva. No se revela el motivo. |
scope_insuficiente | 403 | El token no declara los scopes que exige el endpoint; detalles lista los que faltan. |
permiso_insuficiente | 403 | El sujeto (usuario o aplicación) no tiene el permiso Casbin del endpoint. |
entrada_invalida | 400 | El cuerpo no es JSON/XML válido, trae campos no admitidos o le falta un dato obligatorio. |
escalada_de_scopes | 403 | Se intentó crear una API key con scopes que el token que llama no tiene. |
recurso_no_encontrado | 404 | El recurso (catálogo, aplicación o sujeto de un acceso) no existe. |
empresa_no_encontrada | 404 | No hay emisor con ese RFC o el principal no tiene acceso a él. |
rfc_duplicado | 409 | Ya existe un sello registrado para ese RFC: hay que pedir acceso a quien lo administra. |
slug_duplicado | 409 | Ya existe una aplicación con ese slug. |
acceso_denegado | 403 | El rol de acceso a la empresa (emitir) no permite la operación, o se intentó revocar el acceso propio. |
password_incorrecta | 422 | La contraseña no descifra la llave privada del CSD. |
llave_no_corresponde | 422 | La llave privada no corresponde al certificado. |
certificado_vencido | 422 | El certificado no está vigente. |
regimen_invalido | 422 | El régimen fiscal no existe en el catálogo c_RegimenFiscal. |
config_secret | 500 | Falta o es inválida la clave CFDI_KEY_SECRET del servidor. |
empresa_sin_agente | 422 | La empresa no tiene configurada su ruta en el agente de timbrado. |
agente_no_disponible | 422 | No hay ningún agente de timbrado conectado. |
proveedor_error | 502 | El proveedor de timbrado rechazó la operación; detalles lleva su código numérico. |
cuenta_suspendida | 403 | La cuenta maestra está suspendida: sus usuarios y API keys dejan de operar. |
licencia_vencida | 402 | La licencia de la cuenta no está vigente o no se pudo confirmar. |
sat_rechazo | 422 | El SAT respondió pero no pudo procesar la consulta del estado del CFDI. |
sat_no_disponible | 502 | No se pudo comunicar con el WS público del SAT. |
documento_no_encontrado | 404 | El documento CFDI no existe o pertenece a otra cuenta. |
error_interno | 500 | Fallo no previsto del servidor. |
Migrar una integración existente
Las API keys creadas antes de este modelo siguen funcionando: conservan su usuario como sujeto y recibieron por migración los scopes que ya ejercían (catalogos:read, cfdi:validar, cfdi:generar, cfdi:consulta y actualizaciones:read). Para pasarla al modelo nuevo:
- Registre la aplicación y cree su key con los scopes mínimos.
- Conceda a la aplicación el acceso
emitir(oadministrar) de los RFC que ya usaba. - Cambie el token en el consumidor y verifique con
GET /api/v1/empresas. - Revoque la key personal anterior.
El único cambio obligatorio e inmediato para los consumidores es la forma del envelope de error: antes {"error":"texto"} (y {"codigo","mensaje"} en los fallos del proveedor de timbrado), ahora {"error":{"codigo","mensaje","detalles"}}.