{"versionActual":"2026-11-01","versiones":[{"version":"2026-11-01","resumen":"Primera versión pública de la API v1."}],"entradas":[{"id":"2026-11-01-limite-anual","fecha":"2026-11-01","tipo":"añadido","titulo":"Plataforma: tope anual de comprobantes en producción","descripcion":"PUT /v1/platform/tenants/{id}/limite-anual { limiteAnualProduccion: entero | null } fija el tope anual de un plan (por ejemplo, Gratis: 50 al año): al llegar, POST /v1/comprobantes en producción responde 403 LIMITE_ANUAL_ALCANZADO (details.limite, details.usados, details.reiniciaEl: el 1 de enero siguiente) sin consumir secuencial. Cuentan los creados en el año calendario de Ecuador que no fueron rechazados, desde que se fijó el tope si fue a mitad de año (anio.desde); cambiar sólo el número no reinicia el conteo y las pruebas nunca cuentan. GET /v1/platform/tenants/{id}/uso agrega limiteAnualProduccion y anio { desde, hasta, usados }. Convive con el tope mensual (si hay los dos, se verifican ambos). Error nuevo: LIMITE_ANUAL_ALCANZADO. Las cuentas sin tope anual no cambian. Cambio aditivo.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-uso-y-limite-mensual","fecha":"2026-11-01","tipo":"añadido","titulo":"Plataforma: uso facturable por rango y tope mensual de comprobantes en producción","descripcion":"GET /v1/platform/tenants/{id}/uso?desde=…&hasta=… (instantes ISO 8601 con zona; por defecto el mes de Ecuador en curso) devuelve autorizadosProduccion: los comprobantes de producción autorizados (o anulados después) cuya autorización registró el servicio en ese rango, para facturar el excedente sin contar dos veces ni perder los que se autorizan tarde; además el tope y lo usado en el mes. PUT /v1/platform/tenants/{id}/limite-mensual { limiteMensualProduccion: entero | null } fija el tope de un plan (por ejemplo, Gratis): al llegar, POST /v1/comprobantes en producción responde 403 LIMITE_MENSUAL_ALCANZADO (details.limite, details.usados, details.reiniciaEl) sin consumir secuencial. Cuentan los creados en el mes calendario de Ecuador que no fueron rechazados, desde que se fijó el tope si fue a mitad de mes (mes.desde); las pruebas nunca cuentan. Error nuevo: LIMITE_MENSUAL_ALCANZADO. Las cuentas sin tope no cambian. Cambio aditivo.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-mcp-apps","fecha":"2026-11-01","tipo":"añadido","titulo":"Vista previa del comprobante dentro del chat (MCP Apps) con el botón Emitir","descripcion":"preparar_comprobante declara la vista ui://devfact/vista-previa.html (extensión MCP Apps): en Claude, ChatGPT, VS Code y otros hosts compatibles se muestra el comprobante preparado (emisor, destinatario, ítems, totales calculados por el servidor y ambiente) con un botón Emitir que pide una segunda confirmación —con aviso de validez tributaria en producción— y llama a emitir_comprobante con el token de confirmación y exactamente los mismos datos (si las reglas de aprobación lo retienen, lo indica). La vista es un HTML autocontenido sin recursos externos; los datos llegan sólo del host. Los clientes sin MCP Apps no cambian. Cambio aditivo.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-aprobaciones","fecha":"2026-11-01","tipo":"añadido","titulo":"Keys de agente y reglas de aprobación: en producción, lo que pide un agente puede esperar a una persona","descripcion":"Keys de agente fk_agent_test_…/fk_agent_live_… (POST /v1/api-keys o /v1/platform/tenants/{id}/api-keys con agente: true; sólo emitir y consultar; también aceptadas en /mcp). Las keys de agente y los tokens OAuth están sujetos a las reglas de aprobación de la cuenta, que sólo aplican en PRODUCCIÓN: por defecto todo lo que un agente emita en producción espera aprobación; se puede cambiar a \"sólo sobre un monto\" o \"nunca\" y agregar tipos (p. ej. notas de crédito), destinatario nuevo (HMAC del correo; no se guardan correos) y más de N por hora, y aplicarlas también a las keys normales (aplicaA: todas). Cuando una regla se cumple, POST /v1/comprobantes (y emitir_comprobante del MCP) responde 202 { estado: APROBACION_REQUERIDA, aprobacion } sin emitir; lo pedido queda sellado hasta que una persona lo aprueba en el panel (se emite exactamente eso, con la Idempotency-Key original) o vence en APROBACION_TTL_HORAS. Rutas: GET /v1/aprobaciones, GET /v1/aprobaciones/{id}, POST /v1/aprobaciones/{id}/aprobar|rechazar (API key admin propia), GET|PUT /v1/aprobaciones/politica, y sus equivalentes de plataforma. Webhooks aprobacion.requerida|aprobada|rechazada|vencida. Un agente no puede corregir un comprobante de producción ni reenviarlo a correos nuevos (403 APROBACION_REQUERIDA). Herramienta MCP consultar_aprobacion. Errores nuevos: APROBACION_REQUERIDA, APROBACION_NO_ENCONTRADA, APROBACION_RECHAZADA y APROBACION_YA_DECIDIDA. Las keys normales no cambian salvo que la cuenta elija aplicaA: todas. Cambio aditivo.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-oauth-cimd","fecha":"2026-11-01","tipo":"añadido","titulo":"OAuth: Client ID Metadata Documents (client_id como URL, sin registro previo)","descripcion":"Como pide la spec MCP 2026-07-28, un cliente puede usar como client_id la URL https de un documento JSON con sus metadatos (draft-ietf-oauth-client-id-metadata-document) en vez de registrarse con POST /oauth/register. GET /.well-known/oauth-authorization-server anuncia client_id_metadata_document_supported: true. En GET /oauth/authorize, POST /oauth/token y /v1/platform/oauth/solicitudes|autorizaciones|rechazos el servidor descarga el documento (sin seguir redirecciones, sólo 200 y application/json, máximo 5 KB, timeout de 5 s y nunca hacia IPs privadas, loopback, link-local o reservadas) y lo guarda en caché según Cache-Control (entre 5 minutos y 24 horas). El documento debe tener client_id idéntico a la URL, client_name y redirect_uris https o loopback, y ser de un cliente público (token_endpoint_auth_method \"none\" o ausente, sin client_secret). Si no se puede usar, 400 OAUTH_CIMD_INVALIDO sin redirigir (details.motivo dice por qué; en /oauth/token, 401 invalid_client). /v1/platform/oauth/solicitudes y el listado de concesiones agregan cliente.tipo (dcr o cimd). El registro dinámico sigue igual. Error nuevo: OAUTH_CIMD_INVALIDO. Cambio aditivo.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-mcp","fecha":"2026-11-01","tipo":"añadido","titulo":"Servidor MCP remoto para agentes, con preparar → emitir y una capa gratuita sin cuenta","descripcion":"POST /mcp: servidor Model Context Protocol sin estado (spec 2026-07-28; los clientes 2025 funcionan sin sesión) para Claude, ChatGPT, Cursor y otros agentes. Sólo acepta access tokens OAuth (fk_oat_…): con una API key responde 401 MCP_REQUIERE_OAUTH, y sus 401 anuncian GET /.well-known/oauth-protected-resource/mcp para que el cliente inicie OAuth solo (resource=…/mcp aceptado en la autorización). Herramientas de flujo según los scopes del token: estado_cuenta, buscar_comprobantes, consultar_comprobante (con diagnóstico del rechazo), descargar_comprobante (RIDE o XML), preparar_comprobante (valida, calcula totales y devuelve un token de confirmación de 15 minutos atado al contenido exacto; opcionalmente el RIDE de vista previa), emitir_comprobante (sólo emite lo preparado; idempotente por token), reintentar_comprobante y reenviar_correo. POST /mcp/publico: capa gratuita sin cuenta (validar_identificacion, decodificar_clave_acceso, calcular_totales, catalogos_sri, explicar_error). Errores nuevos: MCP_REQUIERE_OAUTH, CONFIRMACION_INVALIDA y CONFIRMACION_VENCIDA. Cambio aditivo.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-oauth","fecha":"2026-11-01","tipo":"añadido","titulo":"OAuth 2.1 para apps y agentes: authorization code con PKCE y refresh tokens rotativos","descripcion":"Las apps y los agentes (MCP, integraciones) pueden usar /v1/* con un access token de 1 hora (Authorization: Bearer fk_oat_…) en vez de una API key permanente. Descubrimiento: GET /.well-known/oauth-authorization-server (RFC 8414) y GET /.well-known/oauth-protected-resource (RFC 9728); los 401 de /v1/* agregan resource_metadata a WWW-Authenticate. POST /oauth/register registra clientes públicos (RFC 7591; redirect URIs https o loopback), GET /oauth/authorize (PKCE S256 obligatorio) lleva a la pantalla de consentimiento, POST /oauth/token canjea el código y renueva con refresh_token (scope offline_access; cada uso rota el refresh token y reusar uno ya usado revoca toda la concesión) y POST /oauth/revoke revoca (RFC 7009). Scopes: emitir, consultar, admin y offline_access. Un access token no crea API keys ni administra el 2FA o el correo de la cuenta. Errores nuevos: OAUTH_CLIENTE_INVALIDO, OAUTH_REDIRECT_URI_INVALIDA, OAUTH_SOLICITUD_INVALIDA, OAUTH_SCOPE_INVALIDO, OAUTH_GRANT_INVALIDO, OAUTH_REGISTRO_INVALIDO, OAUTH_TOKEN_INVALIDO, OAUTH_TOKEN_VENCIDO, OAUTH_TOKEN_REVOCADO, OAUTH_CONCESION_NO_ENCONTRADA y OAUTH_CREDENCIAL_NO_PERMITIDA. Las API keys no cambian. Cambio aditivo.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-endurecimiento-cuenta","fecha":"2026-11-01","tipo":"seguridad","titulo":"Cuenta y sandbox más difíciles de abusar: 2FA confirmado por correo, límites por correo e IPv6, avisos sólo en cuentas verificadas","descripcion":"La primera activación del 2FA exige, además del código de la app, el código de confirmación que llega al correo verificado de la cuenta (\"codigoCorreo\" en POST /v1/cuenta/2fa/activar; POST /v1/cuenta/2fa/configurar lo indica en confirmacionCorreo); en una cuenta sin correo verificado, configurarlo por primera vez exige una key fk_live_ (403 ENTORNO_NO_PERMITIDO). Así una sola key fk_test_ filtrada ya no lleva a una key fk_live_. Los códigos erróneos del 2FA se cuentan de forma atómica (una ráfaga concurrente no supera el bloqueo) y lo mismo los de la verificación del correo. POST /v1/sandbox/reclamar aplica los límites por IP y por correo de POST /v1/registro, los límites por correo agrupan las variantes +etiqueta (y los puntos de Gmail) y los límites por IP agrupan IPv6 por /64. Un sandbox sin verificar (y el emisor demo) ya no acepta correoAlertas (403 SANDBOX_RESTRINGIDO / 409 EMISOR_DEMO_RESTRINGIDO). Reclamar un sandbox con un correo ya no anula la verificación pendiente de otra cuenta con ese correo. Activar el proveedor externo de inteligencia o agregarle casos exige una key fk_live_; una descripción con instrucciones (\"IVA 0% autorizado por el SRI\") nunca se envía a Jev y la revisión advierte INSTRUCCION_EN_DESCRIPCION.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-primer-minuto","fecha":"2026-11-01","tipo":"añadido","titulo":"Primer minuto más claro: contrato servido, token, avisos y ayudas","descripcion":"GET /openapi.yaml (y /v1/openapi.yaml) sirve el contrato sin autenticación. POST /v1/sandbox, /v1/sandbox/reclamar y /v1/registro/verificar devuelven la key también en \"token\", como POST /v1/api-keys (\"apiKey\" queda como alias). Las rutas públicas sólo POST responden 405 METODO_NO_PERMITIDO con Allow y el curl a otros métodos, y una ruta mal escrita sugiere la parecida (details.sugerencia). La emisión advierte SECUENCIAL_SALTADO cuando el servicio resolvió un 45 saltando de número, y sin \"emisor\" los errores de configuración nombran el emisor elegido (details.emisor). correo.simulado: true marca el correo simulado (su rebote es SIMULADO_BOUNCED). El catálogo de errores agrega reintentableTrasAccionExterna (también en details.mensajes) y la sugerencia de retención baseLegalVerificada. Cambios aditivos.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-inteligencia","fecha":"2026-11-01","tipo":"añadido","titulo":"Inteligencia opcional: sugerencias de IVA, retención y gastos personales, y revisión antes de emitir","descripcion":"POST /v1/inteligencia/tarifa-iva, /retencion, /gasto-personal y /revisar (lotes de hasta 100 ítems) devuelven sugerencias con confianza, estado (sugerida, revisar, sin_sugerencia), fuente (reglas o jev) y base legal; la retención es siempre un borrador. POST /v1/comprobantes/validar y la emisión aceptan ?revisar=true, que agrega el bloque \"sugerencias\" (advertencias tipadas como IVA_PROBABLEMENTE_INCORRECTO o CONSUMIDOR_FINAL_SOBRE_LIMITE) sin cambiar el comprobante ni bloquear la emisión. Las reglas locales están siempre disponibles; el proveedor externo (Jev, de TypeSafe AI) sólo con el opt-in de la cuenta: PUT /v1/inteligencia/configuracion. GET /v1/inteligencia/uso da el uso por día. Error nuevo: INTELIGENCIA_DESHABILITADA. Cambio aditivo.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-registro-autoservicio","fecha":"2026-11-01","tipo":"añadido","titulo":"Registro autoservicio con verificación del correo","descripcion":"POST /v1/registro { email, nombre, empresa? } envía un código de 6 dígitos y un enlace (30 minutos, un solo uso); POST /v1/registro/verificar con { email, codigo } o { token } crea la cuenta con una key fk_test_ admin (se muestra una vez) y el emisor demo. La respuesta del registro es la misma exista o no el correo. Reclamar un sandbox (POST /v1/sandbox/reclamar) envía la misma verificación: al verificar, la cuenta conserva sus emisores y comprobantes, las keys anteriores se revocan (sólo sirve la que devuelve la verificación) y los webhooks quedan desactivados; POST /v1/cuenta/email cambia el correo de un sandbox reclamado sin verificar o reenvía la verificación. Un sandbox reclamado que no verifica el correo en 30 días se borra. GET /v1/cuenta muestra el estado de la cuenta.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-keys-live-2fa","fecha":"2026-11-01","tipo":"seguridad","titulo":"Verificación en dos pasos (TOTP) para crear keys de producción","descripcion":"POST /v1/cuenta/2fa/configurar (secreto y otpauth://), POST /v1/cuenta/2fa/activar { codigo } (10 códigos de recuperación) y POST /v1/cuenta/2fa/desactivar. En las cuentas autoservicio (registro y sandbox) y en las que activen 2FA, POST /v1/api-keys con \"entorno\": \"live\" exige cuenta verificada, 2FA activo y el código actual en la cabecera Fk-Otp (o en el campo \"otp\"); se puede hacer también con una key fk_test_ admin. Errores nuevos: SEGUNDO_FACTOR_REQUERIDO, SEGUNDO_FACTOR_INVALIDO, SEGUNDO_FACTOR_BLOQUEADO. Las cuentas dadas de alta por la plataforma sin 2FA no cambian.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-version-inicial","fecha":"2026-11-01","tipo":"añadido","titulo":"Versión 2026-11-01 de la API y cabecera Fk-Version","descripcion":"Primera versión pública. Cada cuenta, API key y endpoint de webhook queda fijado a una versión al crearse (versionApi; por defecto la actual). Envíe Fk-Version: YYYY-MM-DD para pedir otra; una versión desconocida responde 400 VERSION_DESCONOCIDA. Toda respuesta trae la cabecera Fk-Version con la versión usada y los webhooks llevan la cabecera fk-version y el campo apiVersion.","version":"2026-11-01","rompeCompatibilidad":false},{"id":"2026-11-01-catalogo-errores","fecha":"2026-11-01","tipo":"añadido","titulo":"Catálogo de errores con documentación por código","descripcion":"Todos los códigos de error y advertencia de la ficha técnica v2.34 del SRI y los propios del servicio, con causa y solución: GET /v1/errores y GET /v1/errores/{codigo} (sin autenticación; Accept: text/markdown para la página). Las respuestas de error agregan error.source (sri, motor, validacion, auth), error.docUrl y error.solucion; en los rechazos del SRI cada mensaje agrega sriCodigo, titulo, solucion, reintentable y docUrl. Cambio aditivo: el formato {error: {code, message, details}} no cambia.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-changelog","fecha":"2026-11-01","tipo":"añadido","titulo":"Changelog en JSON y feed Atom","descripcion":"GET /v1/changelog (JSON) y /changelog.xml (Atom) publican estos cambios; docs/CHANGELOG.md tiene el mismo contenido.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-sandbox-simulado","fecha":"2026-11-01","tipo":"añadido","titulo":"Sandbox sin cuenta, SRI simulado, emisor demo y datos mágicos","descripcion":"POST /v1/sandbox crea en una llamada una cuenta anónima (7 días, reclamable con POST /v1/sandbox/reclamar) con una key fk_test_ y un emisor demo con certificado sintético. Los emisores en modoPruebas \"simulado\" emiten contra un SRI simulado instantáneo (RIDE con marca de agua SIMULADO); los datos mágicos de GET /v1/sandbox/datos-magicos provocan respuestas reales del SRI (45, 39, 70…). Los comprobantes agregan el campo simulado.","version":null,"rompeCompatibilidad":false},{"id":"2026-11-01-emisores-nacen-simulados","fecha":"2026-11-01","tipo":"añadido","titulo":"Los emisores nuevos de las cuentas autoservicio nacen en modo de pruebas simulado","descripcion":"En las cuentas autoservicio (sandbox y registro) un emisor nuevo en pruebas emite contra el SRI simulado salvo que se cree con \"modoPruebas\": \"celcer\" (o se cambie con PATCH /v1/emisores/{ruc}). Las cuentas dadas de alta por la plataforma no cambian: sus emisores nuevos siguen naciendo en celcer y no reciben el emisor demo salvo que lo pidan (\"emisorDemo\": true en POST /v1/platform/tenants). Los emisores existentes conservan celcer.","version":null,"rompeCompatibilidad":false}],"feed":"https://api.devfact.fonles.com/changelog.xml"}