Documentación

La API de CopilotGestoria

La contabilidad, la facturación y los modelos fiscales de tus clientes, en tu propia web, app o programa de gestión.

Versión del contrato 2026-09-05 · Especificación OpenAPI

Empezar en cinco minutos

  1. El gestor crea una clave desde su panel, en Integraciones → API y desarrolladores.
  2. Marca qué permisos lleva y sobre qué clientes puede operar.
  3. Copia la clave: se muestra una sola vez.

Tu primera llamada, que no consume cupo y te dice qué puedes hacer:

curl https://www.copilotgestoria.com/api/public/v1/status \
  -H "Authorization: Bearer cg_live_TU_CLAVE"

Las tres cabeceras

CabeceraCuándoPara qué
Authorization Siempre Bearer cg_live_… — quién eres.
X-CG-Client Casi siempre Sobre qué cliente actúas. Los válidos salen en GET /clients.
Idempotency-Key POST, PUT y PATCH Un UUID por operación. Obligatoria, salvo en las tres escrituras de webhooks, donde es opcional. Los DELETE no la usan. Ver abajo.

Por qué la idempotencia es obligatoria

En la mayoría de las API es opcional. Aquí no, y el motivo es concreto: emitir una factura consume un número correlativo de serie y añade un eslabón a la cadena de huellas de VeriFactu. Eso no se deshace. Si tu petición agota el tiempo de espera y reintentas sin clave, no obtienes un registro repetido: obtienes una segunda factura real que solo se corrige emitiendo una rectificativa.

Con clave, el comportamiento es predecible:

  • Reintento con la misma clave y los mismos datos → te devolvemos la misma respuesta, sin volver a ejecutar nada.
  • Misma clave con datos distintos → 409 con idempotency_key_reutilizada. Nunca te devolvemos en silencio el resultado anterior.
  • Guardamos el resultado durante 24 horas, también si fue un error del servidor (500).
  • No guardamos lo que no dejó nada hecho y se arregla reintentando: los errores de validación, los 503 y los conflictos transitorios como operacion_en_curso o emision_no_posible. En esos casos, reintenta con la misma clave.
curl -X POST https://www.copilotgestoria.com/api/public/v1/invoices \
  -H "Authorization: Bearer cg_live_TU_CLAVE" \
  -H "X-CG-Client: 4821" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "serie": "A",
    "ejercicio": 2026,
    "fecha_expedicion": "2026-09-05",
    "tipo_factura": "F1",
    "receptor_nif": "B12345678",
    "receptor_nombre": "Cliente Ejemplo SL",
    "lineas": [
      {"concepto": "Servicios de consultoría", "cantidad": 1, "precio_unitario": 1000, "iva_pct": 21}
    ]
  }'

Lo que tarda va en diferido

Hay operaciones que no caben en una petición HTTP y responden 202 en vez de 201: subir un documento al OCR, generar los modelos anuales (100, 180, 190, 193, 200, 202, 347, 349 y 390), importar un extracto bancario y importar un CSV de clientes. Devolverte un corte de conexión a mitad sería dejarte sin saber si tu factura entró.

El 202 trae un trabajo con su consultar_en. Pregunta por él hasta que el estado deje de ser pending o running:

EstadoQué significa
pendingAceptado, todavía no ha empezado.
runningEn marcha.
succeededListo. resultado trae el mismo recurso que devolvería la vía directa.
failedFalló. error.code dice por qué; no se reintenta solo.
curl -X POST https://www.copilotgestoria.com/api/public/v1/documents \
  -H "Authorization: Bearer cg_live_TU_CLAVE" \
  -H "X-CG-Client: 4821" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F file=@factura.pdf
→ 202 { "object": "job", "id": "…", "estado": "pending", "consultar_en": "…/jobs/…" }

curl https://www.copilotgestoria.com/api/public/v1/jobs/<id> -H "Authorization: Bearer cg_live_TU_CLAVE"
→ 200 { "object": "job", "estado": "succeeded", "resultado": { … } }

GET /jobs/{id} responde 200 también cuando el trabajo falló: que algo saliera mal no es un fallo de tu consulta. Consultar un trabajo no exige ningún permiso añadido — ya lo diste al pedir la operación.

Paginación

Por cursor, nunca por número de página. Si alguien inserta una factura mientras recorres un ejercicio, con page=3 te saltarías o repetirías registros — y en una sincronización contable eso es un descuadre que aparece semanas después.

limit: de 1 a 100, 25 por defecto; fuera de rango, 400 limite_no_valido. Usa el next_cursor tal cual; uno alterado da 400 cursor_no_valido. Orden: del más reciente al más antiguo. Facturas y documentos admiten modificado_desde para sincronizar solo los cambios.

GET https://www.copilotgestoria.com/api/public/v1/invoices?limit=50
→ { "object": "list", "data": [...], "has_more": true, "next_cursor": "abc123" }

GET https://www.copilotgestoria.com/api/public/v1/invoices?limit=50&cursor=abc123

Cada listado admite además sus propios filtros (por NIF, por fechas, por estado…). Los que acepta cada uno, con lo que significa cada parámetro, están en la especificación OpenAPI, que se genera desde las rutas reales del servidor.

Errores

Todos tienen la misma forma, y todos traen el enlace a la petición concreta:

{
  "error": {
    "type": "permission_error",
    "code": "scope_insuficiente",
    "message": "Esta clave no tiene el permiso «Emitir facturas nuevas...».",
    "doc_url": "https://.../docs/api#errores",
    "request_id": "b2c3d4...",
    "request_log_url": "https://.../user/api/registro?request_id=b2c3d4..."
  }
}

El code es estable: puedes programar contra él aunque cambiemos el texto. El request_id viaja también en la cabecera Request-Id de todas las respuestas, correctas y fallidas: guárdalo en tus trazas y el gestor podrá encontrar esa llamada exacta en su panel.

400: petición mal formada (JSON ilegible —json_invalido—, cabecera ausente, parámetro con formato imposible). 422: valor de campo no válido, con el detalle en error.errors y el nombre de campo que enviaste.

Las fechas van siempre en AAAA-MM-DD (con hora ISO opcional): 05/09/2026 se rechaza, porque según el país significa el 5 de septiembre o el 9 de mayo, y en una factura eso no se adivina. El 401 trae WWW-Authenticate: Bearer. Un 503, o un 500 que pide reintentar con la misma Idempotency-Key, no ha escrito nada: repítelo con esa clave. Cualquier otro 500 se devuelve igual durante 24 horas: comprueba el estado antes de reintentar con otra clave.

Las advertencias que no bloquean (retención poco habitual, serie ajustada…) llegan en la respuesta correcta, en avisos, con su codigo y su mensaje.

Tres códigos que conviene distinguir, porque se arreglan de formas distintas:

CódigoQué pasaQuién lo arregla
scope_insuficiente La clave no lleva ese permiso. El gestor, marcándolo en la clave desde su panel.
permiso_fuera_de_plan La clave sí lo lleva, pero el plan de la gestoría no lo incluye. El error trae scope y plan_minimo. El gestor, cambiando de plan. Marcarlo otra vez no sirve de nada.
sin_consentimiento Ese cliente final no ha autorizado el acceso de integraciones. El gestor, autorizando a ese cliente en la clave.

Límites

Hay dos cosas distintas: el ritmo (peticiones por minuto) y el cupo mensual de lecturas del plan de la gestoría. Escribir no consume cupo; leer sí. Cuando el cupo se agota recibes un 429 con cupo_mensual_agotado y un Retry-After que apunta al día 1 del mes siguiente — no se factura el exceso, se bloquea, para que un bucle tuyo no le genere una factura sorpresa a la gestoría. Las escrituras siguen funcionando.

El ritmo se cuenta por clave y por cliente final. Cada respuesta trae RateLimit-Limit, RateLimit-Remaining y RateLimit-Reset (segundos). Todo 429 añade Retry-After y X-CG-Rate-Limited-Reason con el límite alcanzado:

X-CG-Rate-Limited-ReasonQué límite esCómo se arregla
credential-rate El ritmo por minuto de la clave, según el plan de la gestoría. Bajar el ritmo o espaciar las llamadas.
client-rate El ritmo por minuto sobre un mismo cliente final. Repartir las llamadas entre clientes.
concurrency Más de 8 peticiones abiertas a la vez con la misma clave (demasiadas_simultaneas). Reducir los hilos o encadenar las llamadas.
client-creation-rate Más de 120 altas de clientes por hora con la misma clave. Para volcar una cartera, POST /clients/import.
platform-rate Techo de la plataforma por clave (1.000 por minuto), para frenar bucles. Revisar la integración.
ip-rate Peticiones sin una clave bien formada desde una misma dirección (120 por minuto). Mandar la cabecera Authorization bien formada.

Más de 60 claves no válidas en un minuto desde una misma dirección dan 429 demasiados_intentos_fallidos; una clave válida desde esa dirección sigue funcionando.

Qué incluye cada plan

La API no es la misma en todos los planes, y esto no lo decide la ruta: lo decide el plan de la gestoría, y se comprueba en cada petición. Si una gestoría baja de plan, sus claves siguen existiendo pero dejan de poder hacer lo que ese plan ya no incluye — recibirás permiso_fuera_de_plan, no un error de autenticación.

Gratuito

Sin API.

Starter

Incluye:

  • Ver los clientes autorizados, sus datos fiscales y sus actividades
  • Consultar las facturas emitidas y recibidas
  • Consultar los documentos digitalizados y sus datos extraídos
  • Consultar los gastos y recibos sin factura

Una clave.

Professional

Todo lo de Starter, y además:

  • Atender los derechos del cliente: exportar todo su histórico y borrar su rastro
  • Emitir facturas nuevas en nombre del cliente
  • Enviar documentos a digitalizar, corregirlos y retirarlos
  • Registrar gastos y recibos (quedan en borrador hasta que el gestor confirme)
  • Consultar asientos contables, plan contable y periodos
  • Consultar cuentas, movimientos, importaciones de extractos y propuestas de conciliación
  • Consultar las nóminas generadas
  • Consultar los modelos fiscales y descargar sus ficheros
  • Generar modelos fiscales nuevos
  • Descargar el justificante de los modelos ya presentados ante la AEAT
  • Configurar avisos automáticos hacia un servidor propio
  • Registrar cobros de facturas emitidas y marcar vencimientos como cobrados
  • Consultar los destinatarios y productos que el cliente usa al facturar
  • Dar de alta y modificar destinatarios y productos de facturación
  • Consultar las facturas recurrentes y sus próximas emisiones
  • Crear, modificar, pausar, reanudar y cancelar facturas recurrentes
  • Confirmar gastos en borrador (desde ese momento cuentan en el Modelo 130)
  • Consultar los pagos con retención de IRPF
  • Registrar pagos con retención de IRPF (quedan en borrador)
  • Confirmar pagos con retención de IRPF (desde ese momento cuentan en el Modelo 111)
  • Dar de alta cuentas en el plan contable del cliente
  • Importar extractos bancarios
  • Proponer qué factura corresponde a un movimiento (la conciliación la confirma el gestor)
  • Consultar el calendario de obligaciones fiscales del cliente
  • Consultar las peticiones de documentos que hace el gestor
  • Consultar los webhooks y su historial de entregas
  • Consultar mandatos y remesas SEPA y descargar sus ficheros

Hasta 5 claves.

Enterprise

Todo lo de Professional, y además:

  • Dar de alta clientes, importarlos y corregir sus datos de contacto
  • Dar de alta, modificar y eliminar las actividades económicas (epígrafes IAE) del cliente
  • Registrar y revocar mandatos SEPA y descargar las remesas del cliente
  • Consultar y dar de alta trabajadores y sus contratos (no genera nóminas)
  • Consultar los trabajadores y sus contratos

Hasta 20 claves. Configuración avanzada de la clave: lista de IP autorizadas, caducidad elegida al crearla y avisos de cupo y de caducidad.

Cada operación de la especificación OpenAPI lleva su x-scope (el permiso que exige) y su x-plan (el plan más barato que lo incluye). Los dos se calculan desde el código y desde la tabla de planes, así que no pueden contradecir a lo que hace el servidor.

Permisos

PermisoQué autoriza
clientes:leer Ver los clientes autorizados, sus datos fiscales y sus actividades
clientes:derechos Atender los derechos del cliente: exportar todo su histórico y borrar su rastro Escribe datos
facturas:leer Consultar las facturas emitidas y recibidas
facturas:escribir Emitir facturas nuevas en nombre del cliente Escribe datos
documentos:leer Consultar los documentos digitalizados y sus datos extraídos
documentos:escribir Enviar documentos a digitalizar, corregirlos y retirarlos Escribe datos
gastos:leer Consultar los gastos y recibos sin factura
gastos:escribir Registrar gastos y recibos (quedan en borrador hasta que el gestor confirme) Escribe datos
contabilidad:leer Consultar asientos contables, plan contable y periodos
bancos:leer Consultar cuentas, movimientos, importaciones de extractos y propuestas de conciliación
nominas:leer Consultar las nóminas generadas
modelos:leer Consultar los modelos fiscales y descargar sus ficheros
modelos:escribir Generar modelos fiscales nuevos Escribe datos
modelos:presentar Descargar el justificante de los modelos ya presentados ante la AEAT Escribe datos
webhooks:gestionar Configurar avisos automáticos hacia un servidor propio Escribe datos
facturas:cobros Registrar cobros de facturas emitidas y marcar vencimientos como cobrados Escribe datos
directorio:leer Consultar los destinatarios y productos que el cliente usa al facturar
directorio:escribir Dar de alta y modificar destinatarios y productos de facturación Escribe datos
recurrentes:leer Consultar las facturas recurrentes y sus próximas emisiones
recurrentes:escribir Crear, modificar, pausar, reanudar y cancelar facturas recurrentes Escribe datos
gastos:confirmar Confirmar gastos en borrador (desde ese momento cuentan en el Modelo 130) Escribe datos
irpf:leer Consultar los pagos con retención de IRPF
irpf:escribir Registrar pagos con retención de IRPF (quedan en borrador) Escribe datos
irpf:confirmar Confirmar pagos con retención de IRPF (desde ese momento cuentan en el Modelo 111) Escribe datos
contabilidad:escribir Dar de alta cuentas en el plan contable del cliente Escribe datos
bancos:escribir Importar extractos bancarios Escribe datos
bancos:conciliar Proponer qué factura corresponde a un movimiento (la conciliación la confirma el gestor) Escribe datos
calendario:leer Consultar el calendario de obligaciones fiscales del cliente
comunicacion:leer Consultar las peticiones de documentos que hace el gestor
clientes:escribir Dar de alta clientes, importarlos y corregir sus datos de contacto Escribe datos
actividades:escribir Dar de alta, modificar y eliminar las actividades económicas (epígrafes IAE) del cliente Escribe datos
sepa:gestionar Registrar y revocar mandatos SEPA y descargar las remesas del cliente Escribe datos
nominas:empleados Consultar y dar de alta trabajadores y sus contratos (no genera nóminas) Escribe datos
webhooks:leer Consultar los webhooks y su historial de entregas
sepa:leer Consultar mandatos y remesas SEPA y descargar sus ficheros
trabajadores:leer Consultar los trabajadores y sus contratos

En x-scope, a|b significa que basta con uno; una lista exige todos. Webhooks, SEPA y trabajadores se leen con su permiso de lectura (webhooks:leer, sepa:leer, trabajadores:leer) o con el de gestión.

Los marcados como Escribe datos no escriben con una clave de pruebas (cg_test_). Esa clave lee datos reales del cliente autorizado, pero no escribe sobre ellos; sí puedes gestionar tus destinos de webhook. Emitir una factura consume numeración y firma la cadena de VeriFactu, y eso no se deshace ni siquiera «en pruebas». La única lectura que tampoco admite es la exportación completa de un cliente (GET /clients/{id}/export): saca todo su histórico, y eso no se hace con una clave de pruebas. Para probar escrituras sin tocar datos reales existe el sandbox.

Avisos automáticos (webhooks)

En lugar de preguntar cada pocos minutos, te avisamos. La firma va en la cabecera CG-Signature con el formato t=<marca>,v1=<hmac>, calculada sobre marca + "." + cuerpo crudo.

Cuatro cosas que tu receptor tiene que hacer bien:

  • Comparar la firma en tiempo constante, nunca con ===.
  • Rechazar lo que llegue con más de 5 minutos: si no, cualquiera que capture una entrega puede reproducirla para siempre.
  • Responder exactamente 401 a una firma inválida. Lo comprobamos: antes de activar tu destino te enviamos una firma buena y otra mala a propósito, y si a la mala respondes cualquier otra cosa —un 2xx, pero también un 400 o un 403— no lo activamos, y el mensaje te dice qué código recibimos.
  • Descartar duplicados por el id del evento: puede llegar el mismo más de una vez y no garantizamos el orden.

Qué se te entrega, y qué no:

  • Solo lo de los clientes que el gestor ha autorizado en esa clave y que además han consentido el permiso de lectura del evento. Un aviso de factura exige facturas:leer; uno de documento, documentos:leer. Si el cliente no está, el evento existe pero a ti no te llega.
  • Una clave revocada o caducada deja de recibir en el acto, y lo que tuviera pendiente se da por agotado.
  • Un destino que agota sus reintentos se apaga solo y se avisa al gestor. Para volver a recibir hay que verificarlo otra vez desde el panel: así una caída larga tuya no deja el destino reintentando para siempre.
  • Las entregas quedan registradas: GET /webhooks/{id}/entregas dice qué se intentó y con qué resultado, y se puede reenviar lo que se perdió mientras tu servidor estuvo caído.
EventoCuándo
invoice.emittedSe ha emitido una factura
invoice.verifactu_sentLa factura se ha enviado a la AEAT
invoice.verifactu_acceptedLa AEAT ha aceptado la factura
invoice.verifactu_accepted_with_errorsLa AEAT la ha registrado CON INCIDENCIAS (no es un éxito)
invoice.verifactu_rejectedLa AEAT ha rechazado la factura
invoice.verifactu_needs_reviewEstado desconocido tras el envío: lo revisa el gestor
invoice.annulledSe ha anulado una factura
invoice.payment_recordedSe ha registrado un cobro
document.processedSe ha digitalizado un documento
document.rejectedUn documento no se ha incorporado (duplicado, no es una factura o no se ha podido leer)
document.alert_detectedUn documento tiene incidencias que revisar
tax_model.generatedSe ha generado un modelo fiscal
consent.revokedSe ha retirado la autorización de un cliente para esta integración
expense.confirmedSe ha confirmado un gasto (ya cuenta en el Modelo 130)
irpf_payment.confirmedSe ha confirmado un pago con retención de IRPF (ya cuenta en el Modelo 111)
recurring_invoice.emittedUna factura recurrente ha emitido su factura del periodo
bank_transaction.matchedUn movimiento bancario ha quedado conciliado con una factura
credential.expiringLa clave de la integración se va a apagar por falta de uso

Claves: rotación y caducidad

Una clave se rota, no se sustituye a lo bruto: el gestor pulsa Rotar y nace una nueva con los mismos permisos y los mismos clientes, mientras la anterior sigue funcionando siete días. Eso te da margen para desplegar sin que el cliente se quede sin servicio.

  • Los webhooks pasan a la clave nueva en el mismo momento: no hay que darlos de alta otra vez ni se pierden avisos cuando caduque la anterior.
  • La clave nueva hereda los permisos que el plan de la gestoría incluye hoy. Si la anterior llevaba alguno que el plan ya no incluye, no se pasa, y el gestor recibe el aviso.
  • La anterior nunca dura más de lo que ya le quedaba: si tenía una fecha de caducidad más cercana que esos siete días, se respeta.
  • Una clave ya caducada no se rota: hay que crear una nueva. Rotar no puede devolverle la vida a una clave muerta.

Y una clave que lleva noventa días sin usarse se marca para caducar con treinta de aviso: recibirás un evento credential.expiring con la fecha de corte. Ningún cliente ha retirado su autorización en ese camino, y por eso no se emite consent.revoked. Una integración abandonada no puede dejar un secreto vivo sobre la cartera de una gestoría para siempre.

En el plan Enterprise el gestor puede además ponerle fecha de caducidad a una clave al crearla y limitar desde qué direcciones IP funciona. Si tu integración llega desde fuera de esa lista recibirás un 403 con ip_no_permitida: dile al gestor desde qué IP sales antes de desplegar.

Sandbox: entorno de pruebas

El sandbox es un entorno aparte, con su propia dirección y su propia base de datos, con clientes y facturas ficticios. Se puede emitir, cobrar, dar de alta clientes y recibir webhooks sin afectar a datos reales. Está disponible en los planes Starter, Professional y Enterprise, con todos los permisos de la API. El gestor lo crea desde API y desarrolladores › Sandbox.

Tres tipos de clave

ClaveDónde valeQué hace
cg_live_ProducciónLee y escribe datos reales.
cg_test_ProducciónLee datos reales; no escribe.
cg_sbx_Solo el sandboxLee y escribe datos ficticios aislados.

Una clave del sandbox en producción, o una de producción en el sandbox, responde 401 entorno_incorrecto antes de consultar nada. Toda respuesta del sandbox lleva la cabecera CG-Entorno: sandbox y GET /status incluye un bloque sandbox con la hora que el sandbox cree que es.

Primera llamada en menos de treinta minutos

  1. El gestor crea el sandbox y copia la clave cg_sbx_ (se muestra una sola vez).
  2. Llama a GET /status con Authorization: Bearer <clave>.
  3. Lista los clientes con GET /clients y emite una factura con POST /invoices (cabecera X-CG-Client e Idempotency-Key).
  4. Da de alta un destino de webhook con POST /webhooks y dispara un evento con POST /sandbox/webhooks/fire.

Para no escribir las peticiones a mano, importa la especificación (openapi.json) o la colección de Postman (postman.json) y rellena baseUrl, apiKey y clienteId.

Control del sandbox

  • GET /sandbox: ficha (reloj, reseteos que quedan hoy, topes).
  • POST /sandbox/reset: vuelve a sembrar los datos. Conserva las claves y los destinos de webhook con su secreto. Con cliente resetea solo ese cliente. Con clientes declaras clientes a medida (nombre, tipo, régimen de IVA, territorio, caso…) que se añaden a los de ejemplo y se vuelven a crear en cada reseteo. Hay un máximo de reseteos al día.
  • POST /sandbox/clock/advance (dias, horas o segundos): avanza el reloj de ESTE sandbox. Solo avanza; para volver al presente se resetea. Al avanzar se ejecutan las facturas recurrentes que tocan, los recordatorios de cobro, los vencimientos y la remisión simulada.
  • POST /sandbox/webhooks/fire: dispara un evento con carga realista. escenario permite provocar fallos del receptor: receptor_5xx, receptor_lento o firma_invalida.
  • GET /sandbox/emails: los correos que el sistema habría enviado. En el sandbox no sale ninguno.
  • POST /sandbox/bank-accounts/{id}/movements y /consent-expiry: movimientos bancarios a medida y caducidad del consentimiento.
  • POST /sandbox/sepa-remittances/{id}/simulate: mueve el estado de una remesa (presentada, confirmada, rechazada_parcial, anulada). Los estados finales no se revierten.
  • GET /sandbox/diferencias, GET /sandbox/scenarios, GET /sandbox/preparacion y GET /sandbox/configuracion.

Valores de prueba

La cabecera X-CG-Sandbox-Scenario produce siempre el mismo resultado. Un valor desconocido responde 422 escenario_desconocido con la lista de los válidos.

  • Remisión a la AEAT (al emitir): aceptada, aceptada_con_errores (no cuenta como éxito), rechazada (código 4104), estado_desconocido (nunca se reintenta solo), reintentable, duplicada y espera. También se fijan con el NIF del receptor: 00000001R (aceptada_con_errores), 00000002W (rechazada), 00000003A (estado_desconocido), 00000004G (reintentable), 00000005M (duplicada), 00000006Y (espera).
  • Cobros (en POST /invoices/{id}/payments): cobro_correcto, cobro_parcial, cobro_fuera_de_plazo, cobro_devuelto y cobro_devuelto_con_retraso.
  • Plataforma: limite_de_peticiones (429 con Retry-After), servicio_no_disponible (503) y, en escrituras, operacion_en_curso (409 transitorio, no se guarda), error_interno (500 que la idempotencia guarda) y sin_efecto (500 que no se guarda: reintentar vuelve a ejecutar).
  • Consentimiento: el sandbox incluye un cliente autorizado sin consentimiento (sin_consentimiento) y otro cuyo consentimiento solo cubre la lectura de clientes (consentimiento_no_cubre).

Qué no es igual que en producción

  • Remisión a la AEAT. Simulada. No se envía nada a la AEAT, ni a producción ni a su entorno de pruebas.
  • Validación de NIF contra el censo. Solo dígito de control.
  • Huella y código QR. Cadena propia del sandbox. El QR lleva a una página de verificación del sandbox.
  • Documentos. Marca «Entorno de pruebas, sin validez fiscal» y sin mención VERI*FACTU.
  • Series y numeración. Serie SBX independiente. Se reinicia con el reseteo.
  • Correo. Capturado en una bandeja. No sale ningún mensaje.
  • Cobros y pasarelas. Sin pasarela. El cobro se registra con importes mágicos.
  • Banca. Banco de pruebas con movimientos generados. Sin conexión PSD2.
  • Digitalización (OCR), modelos fiscales y extractos bancarios. No disponible: POST /documents, POST /tax-models, POST /bank-transactions/import y las propuestas de conciliación responden 501 funcion_no_disponible_en_sandbox. Las lecturas funcionan, y cada cliente con facturas trae un Modelo 303 del trimestre anterior ya generado.
  • Cobros devueltos. Los escenarios cobro_devuelto y cobro_devuelto_con_retraso devuelven la factura a pendiente sin generar el asiento contrario.
  • Remesas SEPA. El estado de la remesa lo mueve el integrador con POST /sandbox/sepa-remittances/{id}/simulate, sin asientos de cobro ni de devolución.
  • Tiempo. Reloj simulable hacia delante. Las tareas dependientes del tiempo se ejecutan al avanzarlo.
  • Cupos y límites. Contadores propios. No consumen los del plan.
  • Certificados. No se custodian certificados y la subida está bloqueada. Los registros no llevan firma XAdES.
  • Webhooks. Mismos eventos, firma y reintentos. Secreto propio y campo entorno.
  • Conservación. Archivado tras 90 días sin actividad, con aviso a los 60 y 30 días de gracia.

Recibir webhooks en local

Los eventos del sandbox van a las URL que registres, que deben ser públicas y con HTTPS. Para desarrollar en local, expón tu receptor con un túnel (ngrok o similar) y registra esa dirección. No uses un túnel sin autenticar en producción. Verifica siempre la firma CG-Signature, descarta los duplicados por id y tolera el desorden: el sandbox usa los mismos reintentos que producción. Los eventos llevan el campo environment: sandbox.

Registro de cambios del sandbox

  • 14/10/2026. Primera versión: claves cg_sbx_, datos de ejemplo, remisión a la AEAT simulada, reloj simulable, webhooks de prueba y correo capturado.

Paso a producción

Integración (en el sandbox)

  • La integración envía una versión de API fija y la registra.
  • Todas las escrituras envían Idempotency-Key y los reintentos reutilizan la misma clave tras un 503 o un error de red.
  • Los errores 400, 401, 403 (por permiso y por plan), 404, 409, 422, 429 con Retry-After y 503 están probados con los valores de prueba.
  • Se han probado datos incompletos, no válidos y duplicados.
  • El receptor de webhooks compara la firma en tiempo constante, rechaza marcas de tiempo de más de 5 minutos, responde 401 a una firma no válida, descarta duplicados por identificador y tolera el desorden.
  • Los flujos de consentimiento y revocación del cliente están probados, incluido el error sin_consentimiento.
  • La integración tolera la rotación de claves, con 7 días de convivencia de la anterior.
  • El registro propio de llamadas no guarda datos personales innecesarios.
  • Ningún identificador del sandbox se reutiliza en producción.
  • Se han probado los estados de remisión simulados (aceptada, aceptada con errores, rechazada, pendiente de revisión) y su reflejo en la interfaz propia.

Solicitud

  • Están documentados los datos tratados, su finalidad y el encargo de tratamiento (art. 28 RGPD).
  • Constan el dominio, el contacto técnico, la política de privacidad y el contacto de incidencias.
  • Los permisos solicitados están justificados uno a uno y limitados a lo que se usa.
  • Las claves se guardan en un gestor de secretos, con lista de IP, y no están en el código.
  • Se dispone del informe de preparación del sandbox como evidencia.
  • Las claves con escritura sobre facturas, clientes o exportación de datos llevan su declaración de uso.

Activación (con datos reales)

  • El gestor crea la clave de producción desde su panel y marca los clientes autorizados.
  • Cada cliente final registra su consentimiento para los permisos solicitados.
  • Los destinos de webhook de producción están dados de alta y verificados con la firma real.
  • Las claves de producción sustituyen a las del sandbox en el almacén de secretos del servidor de la integración.
  • La primera escritura se hace con un cliente piloto y con revisión humana.
  • A las 24 horas se revisan las entregas y los errores, y se rota cualquier clave usada durante el desarrollo.

Del sandbox se puede exportar la configuración (destinos de webhook sin secreto y plantilla de permisos) con GET /sandbox/configuracion. No se migran datos, claves, secretos ni consentimientos. Un sandbox sin actividad se avisa a los 60 días, se archiva a los 90 y se elimina tras 30 días de gracia.

Los tres derechos del cliente, por API

Si integras datos fiscales de terceros, antes o después alguien te preguntará qué haces con los suyos. Los tres derechos están resueltos para que puedas responder sin pedírselo a nadie:

DerechoEndpointQué devuelve
Acceso (art. 15) GET /clients/{id}/activity Las últimas llamadas de tu integración sobre ese cliente, con el permiso usado y el resultado, más la fecha del consentimiento.
Portabilidad (art. 20) GET /clients/{id}/export Sus datos, documentos, asientos y modelos en un solo fichero, siempre con la misma forma.
Supresión (art. 17) DELETE /clients/{id}/data Revoca el consentimiento, te desautoriza sobre ese cliente y retira la IP y el navegador de tu rastro de llamadas.

Los dos últimos van bajo su propio permiso, clientes:derechos. No entran en clientes:leer: la portabilidad saca de una vez el histórico que en cualquier otra ruta exige tres permisos distintos, y la supresión borra. El gestor los marca aparte, con aviso, y el cliente tiene que haberlos autorizado.

La supresión borra lo tuyo, no lo del gestor. No toca facturas, asientos ni modelos: son documentación contable con plazos de conservación propios —cuatro años de prescripción tributaria, seis del Código de Comercio— y esa decisión es del gestor desde su panel, no de una integración. Del registro de llamadas se retiran la IP y el navegador, pero las filas se conservan: son la prueba de qué leíste y cuándo, que el art. 17.3.e) deja fuera del derecho de supresión.

Cuántas a la vez

Además del ritmo por minuto hay un tope de 8 peticiones abiertas a la vez por clave. Si lo superas recibes un 429 con demasiadas_simultaneas y X-CG-Rate-Limited-Reason: concurrency: eso no se arregla esperando, se arregla bajando los hilos. Sincronizar con cincuenta conexiones en paralelo no va más rápido; tumba la base de datos de la gestoría.

Lo que esta API no hace, y por qué

  • Anular o rectificar facturas. Una anulación es un registro más en la cadena ante la AEAT y no se deshace; una rectificativa equivocada solo se corrige con otra. Se hacen desde el panel, donde hay una persona.
  • Presentar modelos ante la AEAT. Y no por prudencia comercial: la presentación exige la contraseña del certificado del cliente, tecleada en el momento y que no guardamos en ninguna parte. Exponerla por API significaría que un integrador manejase esa contraseña, y eso no lo vamos a hacer. Presentar ocurre en el panel, con una persona delante.
    Lo que sí puedes: descargar el justificante de lo ya presentado —con su CSV— en GET /tax-models/{id}/receipt, que va bajo el permiso propio modelos:presentar porque acredita el cumplimiento de un cliente ante terceros.
  • Escribir asientos contables. Es donde un error se propaga a los modelos fiscales y de ahí a una declaración. Lo que sí puedes es leer el libro diario y dar de alta subcuentas en el plan contable.
  • Confirmar una conciliación bancaria. Puedes proponerla (POST /bank-transactions/{id}/match-proposals) y el gestor la confirma desde su panel. Dar una factura por cobrada mueve el IVA del trimestre y genera un asiento: esa firma es suya.
  • Cambiar el NIF ni el correo de un cliente. Del NIF cuelga la pertenencia de cada factura —en los documentos no hay cliente, hay NIF—, y el correo es el acceso del cliente a su portal. Los dos se corrigen desde el panel.
  • Borrar de verdad. DELETE /documents/{id} es una baja lógica, y se rechaza si el documento tiene asiento contabilizado, IRPF declarado, cobros o conciliación. La contabilidad no se destruye desde un programa externo.
  • Generar nóminas ni remesas SEPA. Puedes dar de alta trabajadores, contratos y mandatos; calcular una nómina y presentar adeudos al banco lo decide el gestor.
  • Autorizarse a sí misma sobre un cliente. Aunque des de alta un cliente por API, nace sin autorizar en ninguna clave: que una integración pueda concederse acceso a un titular de datos sería justo lo que el consentimiento existe para impedir.