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:

PrincipalSujeto de autorizaciónActor en la bitácora de timbradoCuá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.

  1. Permiso del sujeto (Casbin): lo da el rol —admin, usuario (todo humano con cuenta) o aplicacion—.
  2. 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

ScopeQué permite
catalogos:readConsultar y descargar los catálogos de claves del SAT
actualizaciones:readConsultar el estado de versiones de los catálogos
cfdi:validarValidar CFDI 4.0 (esquema, catálogos y reglas de negocio)
cfdi:generarGenerar y sellar CFDI (y timbrarlos con ?timbrar=true)
cfdi:consultaConsultar el estado de un CFDI timbrado ante el SAT
documentos:readListar y descargar los documentos CFDI guardados (XML y PDF)
empresas:readListar los emisores (empresas) a los que se tiene acceso
empresas:writeDar de alta emisores con su CSD y editar régimen fiscal, liga con el agente y accesos
aplicaciones:readListar las aplicaciones registradas
aplicaciones:writeRegistrar y configurar aplicaciones (reservado a administradores)
apikeys:manageCrear y revocar API keys dentro de los propios scopes
cuentas:readConsultar cuentas maestras y su uso (reservado a administradores de plataforma)
cuentas:writeCrear y suspender cuentas maestras (reservado a administradores de plataforma)

Endpoints de gestión

Método y rutaScopePermisoDescripción
GET /api/v1/empresasempresas:readempresa:readEmisores a los que tiene acceso el token, con su rol de acceso.
GET /api/v1/empresas/:rfcempresas:readempresa:readMetadatos de un emisor.
POST /api/v1/empresasempresas:writeempresa:writeAlta de un emisor con su CSD en base64; concede administrar al que la crea.
PATCH /api/v1/empresas/:rfcempresas:writeempresa:writeEdita regimen_fiscal y/o agente_empresa (rol administrar).
DELETE /api/v1/empresas/:rfcempresas:writeempresa:writeBorra el emisor y sus accesos (rol administrar).
GET /api/v1/empresas/:rfc/agente/pruebaempresas:readempresa:readVerifica la liga con el agente de timbrado de la cuenta.
GET /api/v1/aplicacionesaplicaciones:readaplicacion:readAplicaciones registradas (administradores).
GET /api/v1/aplicaciones/:slugaplicaciones:readaplicacion:readDetalle de una aplicación con sus API keys.
POST /api/v1/aplicacionesaplicaciones:writeaplicacion:writeRegistra una aplicación (administradores).
PATCH /api/v1/aplicaciones/:slugaplicaciones:writeaplicacion:writeEdita el nombre o activa/desactiva la aplicación.
POST /api/v1/aplicaciones/:slug/keysapikeys:manageapikey:manageCrea una API key de la aplicación y devuelve el token una sola vez.
GET /api/v1/api-keysapikeys:manageapikey:manageAPI keys del principal que llama.
POST /api/v1/api-keysapikeys:manageapikey:manageCrea una key propia (personal o de su aplicación).
POST /api/v1/api-keys/:id/revocarapikeys:manageapikey:manageDesactiva una key propia.
GET /api/v1/cuenta/usocuentas:readcuenta:readMeterización de la cuenta del token (por métrica y mes).
GET /api/v1/cuentascuentas:writecuenta:writeCuentas maestras registradas (plataforma).
POST /api/v1/cuentascuentas:writecuenta:writeRegistra una cuenta maestra (plataforma).
PATCH /api/v1/cuentas/:idcuentas:writecuenta:writeEdita 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.json

Si 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

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ódigoHTTPSignificado
token_ausente401Falta el encabezado Authorization: Bearer <token>.
token_invalido401El token no existe, fue revocado, expiró, o su usuario responsable está suspendido o su aplicación inactiva. No se revela el motivo.
scope_insuficiente403El token no declara los scopes que exige el endpoint; detalles lista los que faltan.
permiso_insuficiente403El sujeto (usuario o aplicación) no tiene el permiso Casbin del endpoint.
entrada_invalida400El cuerpo no es JSON/XML válido, trae campos no admitidos o le falta un dato obligatorio.
escalada_de_scopes403Se intentó crear una API key con scopes que el token que llama no tiene.
recurso_no_encontrado404El recurso (catálogo, aplicación o sujeto de un acceso) no existe.
empresa_no_encontrada404No hay emisor con ese RFC o el principal no tiene acceso a él.
rfc_duplicado409Ya existe un sello registrado para ese RFC: hay que pedir acceso a quien lo administra.
slug_duplicado409Ya existe una aplicación con ese slug.
acceso_denegado403El rol de acceso a la empresa (emitir) no permite la operación, o se intentó revocar el acceso propio.
password_incorrecta422La contraseña no descifra la llave privada del CSD.
llave_no_corresponde422La llave privada no corresponde al certificado.
certificado_vencido422El certificado no está vigente.
regimen_invalido422El régimen fiscal no existe en el catálogo c_RegimenFiscal.
config_secret500Falta o es inválida la clave CFDI_KEY_SECRET del servidor.
empresa_sin_agente422La empresa no tiene configurada su ruta en el agente de timbrado.
agente_no_disponible422No hay ningún agente de timbrado conectado.
proveedor_error502El proveedor de timbrado rechazó la operación; detalles lleva su código numérico.
cuenta_suspendida403La cuenta maestra está suspendida: sus usuarios y API keys dejan de operar.
licencia_vencida402La licencia de la cuenta no está vigente o no se pudo confirmar.
sat_rechazo422El SAT respondió pero no pudo procesar la consulta del estado del CFDI.
sat_no_disponible502No se pudo comunicar con el WS público del SAT.
documento_no_encontrado404El documento CFDI no existe o pertenece a otra cuenta.
error_interno500Fallo 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:

  1. Registre la aplicación y cree su key con los scopes mínimos.
  2. Conceda a la aplicación el acceso emitir (o administrar) de los RFC que ya usaba.
  3. Cambie el token en el consumidor y verifique con GET /api/v1/empresas.
  4. 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"}}.