Envío de un archivo de Flux 10 e-reporting generado por ti
Esta guía explica cómo depositar en el PPF un archivo de Flux 10 (e-reporting) que ya genera tu sistema, sin que las facturas subyacentes pasen por la facturación de B2Brouter. B2Brouter valida el archivo contra el XSD oficial v3.2, lo normaliza para el depósito y lo transmite a la DGFiP como Plateforme Agréée, devolviéndote los estados del ciclo CDV por webhook y por API. Es un camino alternativo al pipeline nativo (en el que envías los datos de la factura en JSON y B2Brouter construye el F10); ambos comparten el mismo canal de salida hacia el PPF.
A quién va dirigida esta guía
Sección titulada «A quién va dirigida esta guía»A integradores y editores de software que ya producen su propio <Report> F10 y solo
necesitan un canal acreditado para depositarlo. Si lo que quieres es que B2Brouter genere
el F10 a partir de tus facturas, no uses esta guía: consulta la sección de e-Reporting
(Flux 10) de la guía DGFiP.
Requisitos previos
Sección titulada «Requisitos previos»- Una cuenta francesa en B2Brouter con el Tax Report Setting
dgfiphabilitado. Consulta los pasos 1 y 2 de la guía DGFiP. X-B2B-API-Version: 2026-03-02o posterior.- Los permisos de API para los endpoints de ledgers habilitados en tu plan. Si recibes un
403, abre un tique de soporte para que te los habiliten. - El archivo F10 válido contra el paquete XSD oficial v3.2.
Los ejemplos usan
https://api-staging.b2brouter.net. Para producción, sustitúyelo porhttps://api.b2brouter.net.
Depositar el archivo
Sección titulada «Depositar el archivo»POST /accounts/{ACCOUNT_ID}/ledgers/importEl cuerpo de la petición es el XML en crudo, con Content-Type: application/octet-stream
(cualquier otro content type se rechaza, así evitamos que Rails intente interpretar el
cuerpo como parámetros).
curl --request POST \ --url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/ledgers/import \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: 2026-03-02' \ --header 'Content-Type: application/octet-stream' \ --header 'Idempotency-Key: f10-2026-01-transactions-001' \ --data-binary @flux10-transactions.xmlRespuesta — 201 Created:
{ "ledger": { "id": 68916, "type": "dgfip", "state": "new" }}El id del ledger es el identificador con el que seguirás todo el ciclo de vida. Guárdalo.
Estructura mínima del archivo
Sección titulada «Estructura mínima del archivo»El archivo es un <Report> sin namespace. B2Brouter lee de él tres cosas para decidir si
puede depositarlo: el <Issuer>, el subflujo declarado y la validez XSD.
<?xml version="1.0" encoding="UTF-8"?><Report xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ReportDocument> <Id>tu-identificador</Id> <Name>Déclaration Transactions Janvier 2026</Name> <IssueDateTime> <DateTimeString>202602110400</DateTimeString> </IssueDateTime> <TypeCode>IN</TypeCode> <Sender> <!-- Lo sustituimos por la identidad de PA de B2Brouter al depositar --> <Id schemeId="0238">0426</Id> <Name>PDP426</Name> <RoleCode>WK</RoleCode> <URIUniversalCommunication> <URIID>contact@exemple.fr</URIID> </URIUniversalCommunication> </Sender> <Issuer> <!-- Debe coincidir con el SIREN de la empresa de la cuenta destino --> <Id schemeId="0002">832987911</Id> <Name>METACORTEX</Name> <RoleCode>SE</RoleCode> <URIUniversalCommunication> <URIID>contact@metacortex.fr</URIID> </URIUniversalCommunication> </Issuer> </ReportDocument> <TransactionsReport> <!-- … o <PaymentsReport>, pero no los dos a la vez --> </TransactionsReport></Report>Un solo subflujo por depósito. El PPF transmite transacciones y cobros por separado
(spec v3.2 §3.7.7): un <Report> debe llevar <TransactionsReport> o
<PaymentsReport>. El subflujo declarado determina el modo del ledger. Un report con
ambos, o sin ninguno de los dos, se rechaza.
Validaciones, en orden
Sección titulada «Validaciones, en orden»| # | Comprobación | Si falla |
|---|---|---|
| 1 | Content-Type: application/octet-stream | 406 — Content-Type must be application/octet-stream |
| 2 | Tamaño del cuerpo ≤ 100 Mo (límite IRR_TAILLE del PPF, v3.2 §3.4.2) | 422 — The payload exceeds the PPF file limit of 100 Mo |
| 3 | Validación XSD contra el paquete oficial v3.2 | 422 con el detalle de los errores del esquema |
| 4 | Versión de API ≥ 2026-03-02 | 422 — DGFiP F10 import requires API version 2026-03-02 or later |
| 5 | La cuenta tiene el setting dgfip habilitado | 422 — The account has no enabled DGFiP e-reporting configuration |
| 6 | El SIREN de <Issuer><Id> coincide con la empresa de la cuenta | 422 — The <Issuer> SIREN (…) does not match the account's company |
| 7 | Un único subflujo (transactions o payments) | 422 — The report carries both transactions and payments; split it into two deposits o The report carries neither transactions nor payments |
El elemento raíz decide la autoridad: un <Report> sin namespace se interpreta como F10 de
la DGFiP; cualquier otra raíz se sigue tratando como un ledger Verifactu, de modo que la
importación de Verifactu sigue funcionando en todas las versiones de API.
Idempotencia
Sección titulada «Idempotencia»Envía una cabecera Idempotency-Key con cada depósito. Si no envías ninguna, usamos el
SHA256 del payload, de modo que reenviar un archivo idéntico también es idempotente.
| Escenario | Respuesta |
|---|---|
| Clave nueva | 201 con el ledger creado |
| Misma clave, mismo payload | 200 con el ledger que ya existía; no se crea ningún duplicado |
| Misma clave, payload distinto | 409 — Idempotency-Key already used with a different payload |
La clave es única por cuenta. Las claves de más de 64 caracteres o con caracteres no ASCII se almacenan como resumen SHA256; el comportamiento es el mismo.
Normalización del depósito
Sección titulada «Normalización del depósito»Antes de transmitir, B2Brouter reescribe dos nodos de tu archivo:
<ReportDocument><Id>pasa a ser el id del ledger. Es la clave con la que el PPF correlaciona los CDV de retorno; si se mantuviera un valor elegido por el cliente, un depósito podría reclamar las respuestas de un ledger de otra cuenta.<ReportDocument><Sender>pasa a ser la identidad de Plateforme Agréée de B2Brouter (matricule conschemeId="0238",RoleCodeWK), porque quien deposita en el PPF somos nosotros. La AIFE ha confirmado por escrito que esta normalización de depósito es aceptable.
El resto del archivo no se toca, y el envío original se conserva tal como lo hemos recibido como adjunto de auditoría del ledger.
Envío al PPF
Sección titulada «Envío al PPF»El ledger importado queda en estado new y el cron de envío lo recoge en su primera
ejecución tras la medianoche del día del depósito: en la práctica, se deposita en el PPF
al día siguiente. El transporte es un depósito SFTP al PPF (tar.gz con el envelope F10).
El régimen de IVA no interviene en los archivos importados: la periodización por régimen es una función del pipeline nativo. Aquí la cadencia es la del depósito, y el periodo declarado es el que tú escribes en el archivo.
Seguir el ciclo de vida
Sección titulada «Seguir el ciclo de vida»Estados del ledger
Sección titulada «Estados del ledger»| Estado | CDV del PPF | Significado |
|---|---|---|
new | — | Importado y validado, pendiente de depósito |
sent | — | Depositado en el PPF |
acknowledged | 500 — Reçue | El PPF ha recibido el archivo |
deposited | — | Depósito aceptado por el PPF |
registered | 300 | ✅ Terminal — declaración registrada por la DGFiP |
refused | 301 | ❌ Terminal — rechazada; error_code y error_description traen el motivo |
error | — | Error de transmisión; error_description trae el detalle |
Webhooks (recomendado)
Sección titulada «Webhooks (recomendado)»Suscríbete al evento ledger.state_change para recibir cada transición sin necesidad de
hacer polling. Se dispara al entrar en sent, error, acknowledged, deposited,
registered y refused.
{ "web_hook": { "url": "https://example.com/hooks", "events": ["ledger.state_change"] }}El cuerpo que recibes trae el id de la cuenta y el ledger completo:
{ "account_id": 42, "object": { "object": "ledger", "id": 68916, "state": "registered", "mode": null, "error_code": null, "error_description": null, "document_type_code": "xml.ledger.dgfip.transactions", "sent_at": "2026-07-25T02:04:11.000Z", "created_at": "2026-07-24T10:12:03.000Z" }}Si llegan dos CDV muy seguidos, el estado ya superado no se entrega: siempre recibes el estado vigente, no necesariamente una entrega por cada transición. Consulta el ledger si necesitas confirmar el estado actual. Para la verificación de la firma del webhook, consulta la guía de webhooks.
Consultar el estado
Sección titulada «Consultar el estado»curl --request GET \ --url https://api-staging.b2brouter.net/ledgers/{LEDGER_ID} \ --header 'X-B2B-API-Key: {YOUR_API_KEY}' \ --header 'X-B2B-API-Version: 2026-03-02'{ "ledger": { "id": 68916, "type": "dgfip", "state": "refused", "error_code": "303", "error_description": null, "created_at": "2026-07-24T10:12:03.000Z", "sent_at": "2026-07-25T02:04:11.000Z" }}Descargar el archivo depositado
Sección titulada «Descargar el archivo depositado»GET /ledgers/{LEDGER_ID}/downloadDevuelve el XML normalizado que hemos depositado (con el <Id> y el <Sender>
reescritos), disponible desde el momento de la importación. Es el documento que hay que
conservar como prueba de lo declarado.
Descargar la respuesta del PPF
Sección titulada «Descargar la respuesta del PPF»GET /ledgers/{LEDGER_ID}/download_responseDevuelve el CDV recibido del PPF. Mientras no haya ninguno, devuelve 404.
En modo Sandbox,
downloadydownload_responsedevuelven 503: no hay depósito real ni respuestas del PPF.