Salta al contingut
Log in

v2026-06-26

Changelog

Selecciona aquesta versió enviant X-B2B-API-Version: 2026-06-26 a les capçaleres de la teva petició.

Aquesta versió implementa canvis incompatibles. Cada canvi incompatible està marcat amb l'etiqueta Breaking.

CanviÀrea
Events de webhook per a factures rebudesWebhooks
Crear oficines via POST /accounts amb parent_idComptes
CIN independent per a oficines de contacte 🚨 BreakingContactes
parent_id a les respostes de contacteContactes
Validació més estricta de contactes en crear factures 🚨 BreakingFactures
refuse_reason_code de DGFiP a mark_as i a les respostes de facturaFactures
issue_after_import en crear i importar facturesFactures
Consulta a l'Annuaire francès a /directory/fr 🚨 BreakingDirectori
routing_codes del contacte en línia en crear factures 🚨 BreakingFactures
routing_codes a nivell de factura en crear factures 🚨 BreakingFactures
Base imposable i quota d'impost al llistat de facturesFactures
Filtres del llistat de factures: number ara fa coincidència per prefix i series_codeFactures
Generar informe fiscal retorna 201 Created 🚨 BreakingFactures
Preferència unit_code_format del compteComptes
apply_taxes_per_line per facturaFactures

Dos nous events de webhook per a factures rebudes:

  • received_invoice.created — s'activa quan es crea una nova factura rebuda a la plataforma (p. ex. importada o pujada).
  • received_invoice.state_change — s'activa quan una factura rebuda transiciona a un nou estat.

Subscriu-te a aquests events en configurar un webhook:

{
"web_hook": {
"url": "https://example.com/hooks",
"events": ["received_invoice.created", "received_invoice.state_change"]
}
}

Payload per a received_invoice.created:

{
"invoice_id": 12345,
"account_id": 42,
"state": "received"
}

Payload per a received_invoice.state_change:

{
"invoice_id": 12345,
"event_id": 67890,
"state": "accepted",
"notes": null
}

Crear oficines via POST /accounts amb parent_id

Section titled “Crear oficines via POST /accounts amb parent_id”

POST /accounts ara accepta un camp opcional parent_id. Quan es proporciona, el compte es crea com a oficina (filial) sota el compte pare indicat.

Quan parent_id està establert:

  • El pare ha de ser un compte de nivell superior dins del mateix grup (no pot ser ell mateix una oficina).
  • Els camps obligatoris habituals (name, postalcode, email, address, city, province, phone, tin_value) esdevenen opcionals.
  • El compte creat hereta la integració del grup i es retorna amb parent_id a la resposta.

Retorna 422 Unprocessable Entity amb codi parameter_invalid (paràmetre parent_id) si:

  • El compte pare no es troba o pertany a un grup diferent.
  • El pare és ell mateix una oficina.

Exemple de petició:

POST /accounts
{
"account": {
"parent_id": 12345,
"name": "Paris Establishment",
"address": "1 Rue de la Paix",
"city": "Paris",
"postalcode": "75001",
"province": "Ile-de-France",
"email": "paris@example.com"
}
}

Exemple de resposta:

{
"account": {
"id": 67890,
"parent_id": 12345,
"name": "Paris Establishment",
...
}
}

Notes:

  • tin_value / tin_scheme no es poden establir en una oficina — sempre s'hereten del compte pare. Enviar-los retorna 422 parameter_cannot_change (paràmetre tin_value).
  • cin_value / cin_scheme s'emmagatzemen de forma independent a l'oficina. Si no s'estableixen, l'oficina hereta el CIN del pare a les respostes de lectura.

Totes les respostes d'un únic contacte ara inclouen parent_id: GET /contacts/{id}, POST /accounts/{account}/contacts, PUT /contacts/{id} i DELETE /contacts/{id}.

  • Per als contactes de nivell superior, parent_id és null.
  • Per a les oficines, parent_id conté l'ID del contacte pare.

refuse_reason_code de DGFiP a mark_as i a les respostes de factura

Section titled “refuse_reason_code de DGFiP a mark_as i a les respostes de factura”

POST /invoices/{id}/mark_as ara accepta un paràmetre opcional reason_code en transicionar a l'estat refused. Aquest camp porta un codi de motiu estructurat definit per DGFiP per als rebutjos del procés 210.

Valors acceptats:

ADR_ERR, REF_CT_ABSENT, CMD_ERR, TX_TVA_ERR, MONTANTTOTAL_ERR, CALCUL_ERR, DOUBLON, NON_CONFORME, DEST_ERR, TRANSAC_INC, EMMET_INC, CONTRAT_TERM, DOUBLE_FACT, AUTRE

Exemple de petició:

POST /invoices/{id}/mark_as
{
"state": "refused",
"reason": "Incorrect total amount",
"reason_code": "MONTANTTOTAL_ERR"
}

Exemple de resposta (extracte):

{
"invoice": {
"id": 12345,
"state": "refused",
"refuse_reason": "Incorrect total amount",
"refuse_reason_code": "MONTANTTOTAL_ERR",
"..."
}
}

issue_after_import en crear i importar factures

Section titled “issue_after_import en crear i importar factures”

POST /accounts/{account}/invoices i POST /accounts/{account}/invoices/import ara accepten un indicador opcional issue_after_import. Quan és true i la factura passa les validacions:

  • La factura transiciona a state=issued (cantonada c1) de forma síncrona, dins de la mateixa petició.
  • Si el compte té activada una configuració d'informe fiscal before_send (Verifactu, Ksef, Zatca) amb auto_generate activat i la factura hi qualifica, l'informe fiscal es genera en línia i s'exposa via tax_report_ids a la resposta. Quan auto_generate està desactivat, la factura s'emet igualment però no es crea cap informe — coherent amb el flux d'enviament, aquests informes es gestionen a través de l'API de Tax Reports.
  • Les autoritats que informen després de l'enviament (p. ex. SII, SDI) no s'activen en emetre; els seus informes es creen quan la factura s'envia o es marca com enviada.
  • No s'encua cap job d'enviament en segon pla per a la factura; l'estat a nivell de transport (c2) roman intacte. Els informes fiscals que s'envien automàticament (p. ex. Ksef) segueixen encuant el seu propi job d'enviament.

Mentre la factura roman en state=issued és de només lectura: no es pot actualitzar, eliminar ni revertir a new fins que s'envia o es descarrega.

Si tant send_after_import com issue_after_import són true, send_after_import guanya — enviar ja emet la factura. S'ignora per a documents que no siguin factures.

L'endpoint d'importació massiva POST /accounts/{account}/invoices/imports accepta el mateix indicador issue_after_import (com a camp de formulari multipart). S'aplica per fitxer: cada factura importada que hi qualifica s'emet; un fitxer que no es pot emetre, o l'emissió del qual falla, es reporta en aquest element sense avortar el lot.

Exemple de petició:

POST /accounts/{account}/invoices
{
"issue_after_import": true,
"invoice": {
"number": "F-2026-0001",
"..."
}
}

Base imposable i quota d'impost al llistat de factures

Section titled “Base imposable i quota d'impost al llistat de factures”

GET /accounts/{account}/invoices ara retorna dos camps addicionals per factura:

  • taxable_base — el subtotal de la factura abans d'impostos (float, mateixa moneda que currency).
  • tax_amount — l'import total d'impostos, calculat com la suma de tots els desglossaments d'impost (float).

Exemple de resposta (parcial):

{
"invoices": [
{
"id": 12345,
"number": "INV-2025-001",
"total": 121.0,
"taxable_base": 100.0,
"tax_amount": 21.0,
"currency": "EUR"
}
]
}

Els comptes ara exposen un camp unit_code_format que controla la llista de codis utilitzada per als codis d'unitat de les línies de factura en llegir i escriure factures a través de l'API.

  • internal (per defecte) — la pròpia llista de codis d'unitat enters de B2Brouter.
  • unece_rec20 — codis de la Recomanació 20 d'UN/ECE (p. ex. HUR, KGM, H87).

Llegeix'l a GET /accounts/{account} i estableix-lo a PUT /accounts/{account}:

{
"account": {
"unit_code_format": "unece_rec20"
}
}

Quan s'estableix a unece_rec20, el camp unit de la línia de factura canvia de tipus: en lloc de l'id enter per defecte, s'intercanvia com un codi UNECERec20 en cadena (p. ex. "HUR"). Es retorna així en les lectures de factura — GET /invoices/{id} i les respostes de create/update — i s'accepta com a codi en les escriptures (creació i importació de factures); els codis desconeguts recauen a other. Les integracions que interpreten unit com a enter han de gestionar la forma en cadena per a aquests comptes.

El valor per defecte internal manté l'id enter, de manera que les integracions existents no es veuen afectades tret que ho activin.


Nou camp apply_taxes_per_line (booleà, nul·lable) a les factures, tant escrivible com retornat a la resposta. Sobreescriu, per factura, la configuració a nivell de compte del mateix nom que controla l'arrodoniment d'IVA:

  • true — els impostos es calculen per línia (cada línia s'arrodoneix i després se sumen).
  • false — els impostos es calculen per factura (la base imposable se suma per tipus, i s'arrodoneix una sola vegada).
  • omès / null — hereta la configuració apply_taxes_per_line del compte (el comportament anterior).

La sobreescriptura només s'honora per a comptes premium. Això permet alinear el mode d'arrodoniment amb administracions públiques que recalculen els totals i rebutgen discrepàncies, sense canviar el valor per defecte a nivell de compte. Consulta la guia de càlcul de factures per a exemples treballats.

Endpoints afectats:

  • GET /invoices/{id} — retorna l'apply_taxes_per_line efectiu.
  • POST /accounts/{account}/invoices — accepta i retorna apply_taxes_per_line.
  • PATCH /invoices/{id} — accepta i retorna apply_taxes_per_line.

CIN independent per a oficines de contacte

Section titled “CIN independent per a oficines de contacte”

🚨 Breaking

POST /accounts/{account}/contacts i PUT /contacts/{id} ara emmagatzemen cin_value i cin_scheme de forma independent a les oficines. Anteriorment, el CIN sempre s'heretava del contacte pare.

Comportament:

  • Si es proporcionen cin_value / cin_scheme, s'emmagatzemen a l'oficina i es retornen a les respostes.
  • Si s'ometen (o s'esborren amb cin_value: ""), l'oficina hereta el cin_value del contacte pare.
  • tin_value / tin_scheme continuen heretant-se del pare i no es poden establir de forma independent.
  • S'aplica la unicitat del CIN dins del compte: enviar un cin_value ja assignat a un altre contacte del mateix compte retorna 422 Unprocessable Entity.

Endpoints afectats:

  • POST /accounts/{account}/contactscin_value / cin_scheme ara s'emmagatzemen quan parent_id està establert.
  • PUT /contacts/{id}cin_value / cin_scheme ara s'emmagatzemen a les oficines.

Exemple: Crear una oficina amb el seu propi CIN

POST /accounts/{account_id}/contacts
{
"contact": {
"parent_id": 12345,
"name": "Branch Office",
"cin_value": "1234567890",
"cin_scheme": 88
}
}

Resposta:

{
"contact": {
"id": 67890,
"parent_id": 12345,
"name": "Branch Office",
"cin_value": "1234567890",
"cin_scheme": "88",
"..."
}
}

Validació més estricta de contactes en crear factures

Section titled “Validació més estricta de contactes en crear factures”

🚨 Breaking

Quan es proporciona un objecte contact en crear una factura, l'API el compara amb els contactes existents per tin_value o cin_value:

  • Si totes les dades coincideixen amb un contacte existent, la factura es crea i s'enllaça amb ell.
  • Si els identificadors d'encaminament difereixen (cin_value, cin_scheme, routing_codes, pin_value, recipient_code), es crea una oficina automàticament.
  • Si només difereixen camps descriptius (address, city, postalcode, etc.), la petició retorna 422 Unprocessable Entity amb codi data_mismatch. Passa update_contact: true (camp de la petició a nivell superior, per defecte false) per fusionar les dades descriptives amb el contacte coincident en lloc d'això — els identificadors d'encaminament i fiscals mai es sobreescriuen d'aquesta manera.
  • Si no es troba cap contacte coincident, es crea un contacte nou. Quan coincideix amb una empresa publicada al directori de B2Brouter, s'enllaça amb ella: si les dades descriptives de la factura coincideixen amb una de les oficines registrades d'aquesta empresa, el contacte s'enllaça amb aquesta oficina; si en difereix, el contacte manté les dades de la factura (enllaçat al directori però no verificat contra ell).
  • update_contact s'ignora per als documents rebuts: una factura entrant mai reescriu les dades d'un contacte existent.

Nota: l'endpoint de contactes és la manera recomanada de crear i mantenir contactes i les seves oficines. Per enllaçar una factura amb un contacte existent sense cap lògica de coincidència, utilitza contact_id directament.


Consulta a l'Annuaire francès a /directory/fr

Section titled “Consulta a l'Annuaire francès a /directory/fr”

🚨 Breaking

GET /directory/fr/{scheme}/{id} ara consulta l'Annuaire francès de la PPF (API REST de la DGFiP) quan l'identificador és un SIREN (9 dígits, esquema 0002), un SIRET (14 dígits, esquema 0009), o una adreça electrònica FRCTC (esquema 0225, format SIREN, SIREN_SIRET, SIREN_SIRET_CodeRoutage o SIREN_Suffix).

L'identificador ha de ser vàlid per al seu esquema — la longitud i el checksum es validen per a cada esquema, no només els francesos. Un identificador que el validador de l'esquema rebutja retorna 422 Unprocessable Entity amb codi invalid_identifier (això inclou un SIREN/SIRET de longitud incorrecta, un checksum fallit, o una adreça FRCTC amb un SIREN/SIRET no vàlid). Els esquemes sense validador s'accepten i es consulten com abans.

Per a l'esquema 0225 el valor s'utilitza tal qual com a identificador d'adreçament de l'Annuaire, i l'empresa resolta es clau per el seu identificador legal subjacent: un SIREN sol resol a cin_scheme 0002, un SIREN_SIRET a 0009 (SIRET), amb l'adreça FRCTC completa mantinguda com a pin_scheme 0225; un code routage s'exposa com a codi d'encaminament cin1 (esquema 0224). Això significa que una consulta 0225 i una posterior consulta 0009/0002 de la mateixa empresa retornen la mateixa entrada de directori.

La consulta és asíncrona per mantenir els temps de resposta en mil·lisegons:

  • Si l'empresa ja és coneguda al directori de B2Brouter, la resposta és 200 OK amb les dades de l'empresa.
  • Si encara no és coneguda, la resposta és 202 Accepted amb un polling_url. Un job en segon pla consulta l'Annuaire i persisteix el resultat. Reintenta la petició al cap d'uns segons — un cop les dades estiguin disponibles, la petició retorna 200 OK.
  • Quan una entrada de directori coneguda no s'ha comprovat en les últimes 24 hores (o mai s'ha enriquit des de l'Annuaire), la resposta continua sent 200 OK immediatament i s'activa una actualització en segon pla (stale-while-revalidate).
  • Després d'un 404 Not Found en un SIREN/SIRET, les peticions posteriors per al mateix identificador retornen 404 durant 15 minuts sense tornar a consultar l'Annuaire. Reintenta després d'aquest període si creus que l'identificador s'ha registrat des de llavors.
  • Si la consulta falla perquè un servei upstream (Annuaire, Peppol) no està disponible, la resposta és 424 Failed Dependency amb codi d'error upstream_unavailable i una capçalera Retry-After — reintenta després del retard indicat. Una fallada upstream transitòria mai es converteix en un 404.

La resposta inclou un nou camp information_flags (array de cadenes) que porta l'estat de registre a l'Annuaire, alineat amb l'especificació CTC de Peppol:

IndicadorSignificat
FR_ASSUJETTI_ACTIVEL'identificador és present i actiu a l'Annuaire
FR_ASSUJETTI_INACTIVEL'identificador és present a l'Annuaire però inactiu
FR_ASSUJETTI_UNKNOWNL'empresa és coneguda per B2Brouter però absent de l'Annuaire

S'estableix exactament un d'aquests indicadors per empresa. Quan una empresa ja coneguda per B2Brouter (p. ex. via Peppol) està absent de l'Annuaire, es retorna amb FR_ASSUJETTI_UNKNOWN perquè l'absència sigui visible. Un identificador desconegut per B2Brouter i absent de l'Annuaire resol a 404 Not Found (vegeu la nota de caché de negació anterior).

Les unitats subordinades (oficines SIRET sota un SIREN) encara no s'exposen a la resposta.

Exemple: empresa encara no coneguda — primera petició

Finestra del terminal
curl --request GET \
--url 'https://app-staging.b2brouter.net/api/v1/directory/fr/0002/123456789' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: 2026-06-26'
{
"status": "processing",
"message": "Querying French PPF Annuaire. Please retry this request.",
"polling_url": "https://app-staging.b2brouter.net/api/v1/directory/fr/0002/123456789"
}

Estat HTTP: 202 Accepted.

Exemple: la mateixa petició uns segons després

{
"name": "SIREN 123456789",
"country": "fr",
"information_flags": ["FR_ASSUJETTI_ACTIVE"],
...
}

Estat HTTP: 200 OK.

Endpoints afectats:

  • GET /directory/fr/{scheme}/{id} — comportament ampliat descrit anteriorment. Els països no francesos no es veuen afectats.

routing_codes del contacte en línia en crear factures

Section titled “routing_codes del contacte en línia en crear factures”

🚨 Breaking

El contact en línia a POST /accounts/{account}/invoices ara accepta un objecte routing_codes — la mateixa forma utilitzada per POST /accounts/{account}/contacts i retornada a les respostes de contacte. Porta fins a cinc Números d'Identificació d'Empresa addicionals (cin1_value/cin1_schemecin5_value/cin5_scheme) a més del cin principal. Si es proporciona cinN_value, cinN_scheme és obligatori (i viceversa). Aquests codis d'encaminament també es retornen dins de routing_codes a l'objecte invoice.contact incrustat a la resposta.

POST /accounts/{account_id}/invoices
{
"invoice": {
"contact": {
"tin_value": "ESA13585625",
"name": "Buyer Full Name AS",
"routing_codes": {
"cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS",
"cin1_scheme": "8017"
}
}
}
}

Canvi incompatible: les claus planes cinN_value / cinN_scheme dins de contact ja no s'accepten. Aquestes peticions retornen 422 Unprocessable Entity. Envia els codis d'encaminament del contacte a través de contact.routing_codes en el seu lloc.


routing_codes a nivell de factura en crear factures

Section titled “routing_codes a nivell de factura en crear factures”

🚨 Breaking

POST /accounts/{account}/invoices i PUT /invoices/{id} ara accepten un objecte routing_codes a nivell superior de la factura — la mateixa forma cin1_value/cin1_schemecin5_value/cin5_scheme utilitzada per contact.routing_codes. Porta els codis d'encaminament propis de la factura (p. ex. els codis de servei DIR3 / Chorus Pro del comprador), independents dels del contacte.

Els codis aplicats es retornen en un objecte routing_codes al nivell superior de la factura de la resposta, de manera que un integrador pot verificar què s'ha emmagatzemat.

POST /accounts/{account_id}/invoices
{
"invoice": {
"number": "F-2026-0001",
"routing_codes": {
"cin1_value": "ESR_MISSION_FACTURES_DEPLACEMENTS",
"cin1_scheme": "8017"
}
}
}

Els camps d'encaminament dedicats que versions anteriors acceptaven al nivell superior de la factura ja no s'accepten. Estableix la ranura routing_codes equivalent en el seu lloc, segons l'esquema:

RanuraDIR3 (esquema 8014)DIRe (8015)EDI/GLN (88)
cin1accounting_unitnadby
cin2managing_unitnaddp
cin3processing_unitnadiv
cin4proponent_unitnadpr
cin5direnadud

Canvi incompatible: al nivell superior de la factura, tant les claus planes cinN_value / cinN_scheme com els camps d'encaminament dedicats (dire, accounting_unit, managing_unit, processing_unit, proponent_unit, nadby, naddp, nadiv, nadpr, nadud) ja no s'accepten, tant a POST /accounts/{account}/invoices com a PUT /invoices/{id}. Aquestes peticions retornen 422 Unprocessable Entity. Envia els codis d'encaminament de la factura a través de l'objecte routing_codes de la factura en el seu lloc. Versions anteriors de l'API encara accepten aquests camps.


Canvis de filtratge a GET /accounts/{account}/invoices:

number ara fa coincidència per prefix

El paràmetre de consulta number ara fa una coincidència de prefix en lloc d'una coincidència exacta. Es retornen les factures el número de les quals comença per el valor proporcionat.

GET /accounts/{account}/invoices?number=INV-2

retorna INV-20, INV-21, … a més d'una factura numerada exactament INV-2.

En versions anteriors, el filtre number requeria el número de factura complet i retornava només una coincidència exacta.

series_code

El paràmetre de consulta series_code filtra factures per codi de sèrie mitjançant coincidència exacta.


Generar informe fiscal retorna 201 Created

Section titled “Generar informe fiscal retorna 201 Created”

🚨 Breaking

POST /invoices/{id}/generate_tax_report ara retorna 201 Created en cas d'èxit en lloc de 200 OK. El cos de la resposta no canvia.

Abans (≤ 2026-04-20): 200 OK

Després (≥ 2026-06-26): 201 Created

Aquest és un canvi incompatible menor per als clients que comproven un estat exacte de 200. Els clients que comproven qualsevol èxit 2xx no es veuen afectats.