Guide E-Reporting avec l'API FactPulse
Ce guide explique comment utiliser l'API E-Reporting de FactPulse pour soumettre vos données de transactions et paiements au PPF (Portail Public de Facturation) via une PA (Plateforme Agréée).
Qu'est-ce que le E-Reporting ?
Le E-Reporting (flux 10) est l'obligation de transmettre à l'administration fiscale les données de transactions qui ne font pas l'objet d'une facture électronique (e-invoicing) :
| Flux | Description | Cas d'usage |
|---|---|---|
| 10.1 | Transactions B2B internationales | Ventes/achats avec entreprises étrangères |
| 10.2 | Paiements B2B internationaux | Encaissements sur factures B2B int. |
| 10.3 | Transactions B2C agrégées | Ventes à des particuliers |
| 10.4 | Paiements B2C agrégés | Encaissements sur ventes B2C |
Endpoints disponibles
Endpoints mono-flux (flux unique)
| Méthode | Endpoint | Description |
|---|---|---|
POST |
/api/v1/ereporting/generate |
Générer un XML e-reporting |
POST |
/api/v1/ereporting/generate/download |
Générer et télécharger le XML |
POST |
/api/v1/ereporting/validate |
Valider les données sans générer |
POST |
/api/v1/ereporting/submit |
Soumettre à la PA |
GET |
/api/v1/ereporting/flux-types |
Lister les types de flux |
Endpoints multi-flux (dépôt agrégé PPF-compliant)
| Méthode | Endpoint | Description |
|---|---|---|
POST |
/api/v1/ereporting/generate-aggregated |
Générer un XML multi-flux |
POST |
/api/v1/ereporting/validate-aggregated |
Valider les données agrégées |
POST |
/api/v1/ereporting/submit-aggregated |
Soumettre un dépôt agrégé |
GET |
/api/v1/ereporting/category-codes |
Lister les codes de catégorie PPF |
Endpoints XML direct
| Méthode | Endpoint | Description |
|---|---|---|
POST |
/api/v1/ereporting/validate-xml |
Valider un XML contre les XSD PPF |
POST |
/api/v1/ereporting/submit-xml |
Soumettre un XML pré-généré |
Codes conformes PPF v3.1
CategoryCode (TT-81) - Catégorie de transactions
| Code | Description | Correspondance cadres facturation |
|---|---|---|
TLB1 |
Livraisons de biens soumises à la TVA | B1, B2, B4, B7 |
TPS1 |
Prestations de services soumises à la TVA | S1, S2, S3, S4, S5, S6, S7 |
TNT1 |
Transactions non taxées (exonérées) | Exonérations |
TMA1 |
Transactions mixtes agrégées | M1, M2, M4 (à décomposer) |
TransmissionTypeCode (TT-4) - Type de transmission
| Code | Description |
|---|---|
IN |
Transmission initiale |
RE |
Transmission rectificative |
TaxDueDateTypeCode (TT-80) - Exigibilité TVA
| Code | Description |
|---|---|
5 |
TVA sur les débits |
29 |
TVA sur les encaissements |
72 |
TVA à la livraison |
Exemples avec Python (requests)
1. Flux 10.3 - Ventes B2C agrégées
import requests
from datetime import date
from decimal import Decimal
# Configuration
BASE_URL = "https://factpulse.fr/api/v1"
TOKEN = "votre_jwt_token"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json"
}
# Données e-reporting B2C - CONFORME PPF v3.1
payload = {
"reportId": "EREPORT-2025-001",
"reportName": "E-Reporting Janvier 2025",
"fluxType": "10.3",
"transmissionType": "IN", # IN=Initial, RE=Rectificatif
"sender": {
"siren": "123456789",
"name": "Ma Société SARL",
"vatId": "FR12345678901" # Optionnel
},
"period": {
"startDate": "2025-01-01",
"endDate": "2025-01-31"
},
"transactions": [
{
"date": "2025-01-15",
"categoryCode": "TLB1", # Code PPF pour livraisons de biens
"taxExclusiveAmount": "10000.00",
"taxAmount": "2000.00",
"taxBreakdown": [
{
"rate": "20.00",
"taxableAmount": "10000.00",
"taxAmount": "2000.00"
}
],
"transactionCount": 150
},
{
"date": "2025-01-31",
"categoryCode": "TPS1", # Code PPF pour prestations de services
"taxExclusiveAmount": "8000.00",
"taxAmount": "440.00",
"taxBreakdown": [
{
"rate": "5.50",
"taxableAmount": "8000.00",
"taxAmount": "440.00"
}
],
"transactionCount": 200
}
]
}
# Générer le XML
response = requests.post(
f"{BASE_URL}/ereporting/generate",
headers=headers,
json=payload
)
if response.status_code == 200:
data = response.json()
print(f"XML généré: {data['xmlSize']} bytes")
print(f"Report ID: {data['reportId']}")
# Sauvegarder le XML
with open("ereporting.xml", "w") as f:
f.write(data["xml"])
else:
print(f"Erreur: {response.json()}")
2. Dépôt agrégé multi-flux (recommandé)
Le PPF attend un fichier unique contenant tous les types de flux. Utilisez /generate-aggregated :
# Dépôt agrégé conforme PPF - un fichier = tous les flux
payload = {
"reportId": "EREPORT-AGG-2025-001",
"reportName": "E-Reporting Agrégé Janvier 2025",
"transmissionType": "IN", # IN=Initial, RE=Rectificatif
"sender": {
"siren": "123456789",
"name": "Ma Société SARL"
},
"period": {
"startDate": "2025-01-01",
"endDate": "2025-01-31"
},
# Flux 10.1 - Factures B2B internationales
"invoices": [
{
"invoiceId": "FAC-2025-INT-001",
"issueDate": "2025-01-10",
"typeCode": "380",
"sellerSiren": "123456789",
"sellerCountry": "FR",
"buyerCountry": "DE",
"taxExclusiveAmount": "50000.00",
"taxAmount": "0.00",
"taxBreakdown": [
{"rate": "0.00", "taxableAmount": "50000.00", "taxAmount": "0.00"}
]
}
],
# Flux 10.3 - Transactions B2C agrégées par catégorie
"transactions": [
{
"date": "2025-01-15",
"categoryCode": "TLB1", # Livraisons de biens
"taxExclusiveAmount": "10000.00",
"taxAmount": "2000.00",
"taxBreakdown": [
{"rate": "20.00", "taxableAmount": "10000.00", "taxAmount": "2000.00"}
],
"transactionCount": 150
},
{
"date": "2025-01-15",
"categoryCode": "TPS1", # Prestations de services
"taxExclusiveAmount": "5000.00",
"taxAmount": "1000.00",
"taxBreakdown": [
{"rate": "20.00", "taxableAmount": "5000.00", "taxAmount": "1000.00"}
],
"transactionCount": 80
}
],
# Flux 10.4 - Paiements B2C agrégés
"transactionPayments": [
{
"paymentDate": "2025-01-20",
"amountsByRate": [
{"rate": "20.00", "amount": "6000.00"}
]
}
]
}
response = requests.post(
f"{BASE_URL}/ereporting/generate-aggregated",
headers=headers,
json=payload
)
if response.status_code == 200:
data = response.json()
print(f"XML généré: {len(data['xml'])} bytes")
print(f"FlowType AFNOR: {data['flowType']}") # MultiFlowReport
print(f"Contenu: {data['contentSummary']}")
3. Flux 10.1 - Factures B2B internationales
payload = {
"reportId": "EREPORT-2025-002",
"fluxType": "10.1",
"transmissionType": "IN",
"sender": {
"siren": "123456789",
"name": "Exportateur SA"
},
"period": {
"startDate": "2025-01-01",
"endDate": "2025-01-31"
},
"invoices": [
{
"invoiceId": "FAC-2025-001",
"issueDate": "2025-01-10",
"typeCode": "380", # Facture standard
"currency": "EUR",
"sellerSiren": "123456789",
"sellerCountry": "FR",
"buyerCountry": "DE", # Allemagne
"buyerVatId": "DE123456789",
"taxExclusiveAmount": "50000.00",
"taxAmount": "0.00", # Exonéré (export intra-EU)
"taxBreakdown": [
{
"rate": "0.00",
"taxableAmount": "50000.00",
"taxAmount": "0.00"
}
]
}
]
}
response = requests.post(
f"{BASE_URL}/ereporting/generate",
headers=headers,
json=payload
)
4. Flux 10.4 - Paiements B2C
payload = {
"reportId": "EREPORT-2025-003",
"fluxType": "10.4",
"transmissionType": "IN",
"sender": {
"siren": "123456789",
"name": "Commerce SARL"
},
"period": {
"startDate": "2025-01-01",
"endDate": "2025-01-31"
},
"aggregatedPayments": [
{
"paymentDate": "2025-01-15",
"amountsByRate": [
{"rate": "20.00", "amount": "12000.00"},
{"rate": "5.50", "amount": "550.00"}
]
}
]
}
5. Récupérer les codes de catégorie PPF
# Lister les codes conformes PPF
response = requests.get(
f"{BASE_URL}/ereporting/category-codes",
headers=headers
)
codes = response.json()
print("Codes de catégorie PPF valides:")
for code in codes["categoryCodes"]:
print(f" {code['code']}: {code['description']}")
# TLB1: Livraisons de biens soumises à la TVA
# TPS1: Prestations de services soumises à la TVA
# TNT1: Transactions non taxées (exonérées)
# TMA1: Transactions mixtes agrégées
6. Validation avant soumission
# Valider sans générer
response = requests.post(
f"{BASE_URL}/ereporting/validate",
headers=headers,
json={"data": payload}
)
result = response.json()
if result["valid"]:
print("Données valides!")
else:
print("Erreurs de validation:")
for error in result["errors"]:
print(f" - {error['field']}: {error['message']}")
7. Validation d'un XML contre les XSD PPF
# Valider un fichier XML pré-généré
with open("mon_ereporting.xml", "rb") as f:
response = requests.post(
f"{BASE_URL}/ereporting/validate-xml",
headers={"Authorization": f"Bearer {TOKEN}"},
files={"xml_file": f}
)
result = response.json()
if result["valid"]:
print("XML conforme aux XSD PPF!")
print(f"FlowType détecté: {result['flowType']}")
else:
print("Erreurs XSD:")
for error in result["errors"]:
print(f" - {error}")
8. Soumettre un XML pré-généré
# Soumettre un XML déjà généré par un autre système
with open("mon_ereporting.xml", "rb") as f:
response = requests.post(
f"{BASE_URL}/ereporting/submit-xml",
headers={"Authorization": f"Bearer {TOKEN}"},
files={"xml_file": f},
data={
"report_type": "combined", # transactions, payments, combined
"tracking_id": "TRACK-2025-001"
}
)
if response.status_code == 200:
result = response.json()
print(f"Flow ID: {result['flowId']}")
print(f"SHA256: {result['sha256']}")
9. Soumission à la PA
# Soumettre avec credentials PA
payload = {
"data": {
"reportId": "EREPORT-2025-001",
"fluxType": "10.3",
"transmissionType": "IN",
# ... reste des données
},
"trackingId": "TRACK-2025-001", # Optionnel, pour suivi
# Credentials PA (optionnel si configurés côté serveur)
"pdpFlowServiceUrl": "https://api.ma-pa.fr/flow/v1",
"pdpTokenUrl": "https://auth.ma-pa.fr/oauth/token",
"pdpClientId": "mon_client_id",
"pdpClientSecret": "mon_secret"
}
response = requests.post(
f"{BASE_URL}/ereporting/submit",
headers=headers,
json=payload
)
if response.status_code == 200:
data = response.json()
print(f"Soumis! Flow ID: {data['flowId']}")
print(f"SHA256: {data['sha256']}")
else:
print(f"Erreur: {response.json()}")
Exemple avec TypeScript/JavaScript
interface EReportingRequest {
reportId: string;
reportName?: string;
fluxType: "10.1" | "10.2" | "10.3" | "10.4";
transmissionType: "IN" | "RE"; // TT-4: Initial ou Rectificatif
sender: {
siren: string;
name: string;
vatId?: string;
};
period: {
startDate: string; // YYYY-MM-DD
endDate: string;
};
transactions?: AggregatedTransaction[];
invoices?: Invoice[];
invoicePayments?: InvoicePayment[];
aggregatedPayments?: AggregatedPayment[];
}
interface AggregatedTransaction {
date: string;
categoryCode: "TLB1" | "TPS1" | "TNT1" | "TMA1"; // Codes PPF
taxExclusiveAmount: string;
taxAmount: string;
taxBreakdown: TaxBreakdown[];
transactionCount?: number;
taxDueDateTypeCode?: "5" | "29" | "72"; // UNTDID codes
}
async function generateEReporting(data: EReportingRequest): Promise<string> {
const response = await fetch("https://factpulse.fr/api/v1/ereporting/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FACTPULSE_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`E-Reporting error: ${error.detail?.message || error}`);
}
const result = await response.json();
return result.xml;
}
// Utilisation
const xml = await generateEReporting({
reportId: "EREPORT-2025-001",
fluxType: "10.3",
transmissionType: "IN",
sender: { siren: "123456789", name: "Ma Société" },
period: { startDate: "2025-01-01", endDate: "2025-01-31" },
transactions: [
{
date: "2025-01-15",
categoryCode: "TLB1", // Code PPF conforme
taxExclusiveAmount: "10000.00",
taxAmount: "2000.00",
taxBreakdown: [
{ rate: "20.00", taxableAmount: "10000.00", taxAmount: "2000.00" },
],
transactionCount: 150,
},
],
});
Exemple avec cURL
# Générer un e-reporting B2C (conforme PPF v3.1)
curl -X POST "https://factpulse.fr/api/v1/ereporting/generate" \
-H "Authorization: Bearer $FACTPULSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reportId": "EREPORT-2025-001",
"fluxType": "10.3",
"transmissionType": "IN",
"sender": {"siren": "123456789", "name": "Ma Société"},
"period": {"startDate": "2025-01-01", "endDate": "2025-01-31"},
"transactions": [{
"date": "2025-01-15",
"categoryCode": "TLB1",
"taxExclusiveAmount": "10000.00",
"taxAmount": "2000.00",
"taxBreakdown": [{"rate": "20.00", "taxableAmount": "10000.00", "taxAmount": "2000.00"}],
"transactionCount": 150
}]
}'
# Lister les codes de catégorie PPF
curl -X GET "https://factpulse.fr/api/v1/ereporting/category-codes" \
-H "Authorization: Bearer $FACTPULSE_TOKEN"
# Générer un dépôt agrégé multi-flux
curl -X POST "https://factpulse.fr/api/v1/ereporting/generate-aggregated" \
-H "Authorization: Bearer $FACTPULSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reportId": "EREPORT-AGG-2025-001",
"transmissionType": "IN",
"sender": {"siren": "123456789", "name": "Ma Société"},
"period": {"startDate": "2025-01-01", "endDate": "2025-01-31"},
"transactions": [
{"date": "2025-01-15", "categoryCode": "TLB1", "taxExclusiveAmount": "10000.00", "taxAmount": "2000.00", "taxBreakdown": [{"rate": "20.00", "taxableAmount": "10000.00", "taxAmount": "2000.00"}], "transactionCount": 150}
],
"invoices": [
{"invoiceId": "FAC-INT-001", "issueDate": "2025-01-10", "typeCode": "380", "sellerSiren": "123456789", "sellerCountry": "FR", "buyerCountry": "DE", "taxExclusiveAmount": "50000.00", "taxAmount": "0.00", "taxBreakdown": [{"rate": "0.00", "taxableAmount": "50000.00", "taxAmount": "0.00"}]}
]
}'
# Valider un XML contre les XSD PPF
curl -X POST "https://factpulse.fr/api/v1/ereporting/validate-xml" \
-H "Authorization: Bearer $FACTPULSE_TOKEN" \
-F "xml_file=@mon_ereporting.xml"
# Télécharger directement le fichier XML
curl -X POST "https://factpulse.fr/api/v1/ereporting/generate/download" \
-H "Authorization: Bearer $FACTPULSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reportId": "...", ...}' \
-o ereporting.xml
Structure des données
Champs communs
| Champ | Type | Requis | Description |
|---|---|---|---|
reportId |
string | Oui | Identifiant unique du rapport |
reportName |
string | Non | Nom descriptif |
fluxType |
enum | Oui | Type de flux (10.1, 10.2, 10.3, 10.4) |
transmissionType |
enum | Non | Type de transmission (IN, RE). Défaut: IN |
sender |
object | Oui | Informations de l'émetteur |
period |
object | Oui | Période de reporting |
Sender (émetteur)
| Champ | Type | Requis | Description |
|---|---|---|---|
siren |
string | Oui | SIREN (9 chiffres) ou SIRET (14 chiffres) |
name |
string | Oui | Raison sociale |
vatId |
string | Non | Numéro TVA intracommunautaire |
Transaction agrégée (flux 10.3)
| Champ | Type | Requis | Description |
|---|---|---|---|
date |
string | Oui | Date de la transaction (YYYY-MM-DD) |
categoryCode |
enum | Oui | Catégorie PPF (TLB1, TPS1, TNT1, TMA1) |
currency |
string | Non | Devise (défaut: EUR) |
taxExclusiveAmount |
string | Oui | Montant HT |
taxAmount |
string | Oui | Montant TVA |
taxBreakdown |
array | Oui | Ventilation par taux de TVA |
transactionCount |
int | Non | Nombre de transactions agrégées |
taxDueDateTypeCode |
enum | Non | Exigibilité TVA (5, 29, 72) |
Facture B2B internationale (flux 10.1)
| Champ | Type | Requis | Description |
|---|---|---|---|
invoiceId |
string | Oui | Numéro de facture |
issueDate |
string | Oui | Date d'émission |
typeCode |
enum | Oui | Type (380=facture, 381=avoir) |
currency |
string | Non | Devise |
sellerSiren |
string | Oui | SIREN du vendeur |
sellerCountry |
string | Non | Pays du vendeur (défaut: FR) |
buyerCountry |
string | Oui | Pays de l'acheteur |
buyerVatId |
string | Non | TVA de l'acheteur |
taxExclusiveAmount |
string | Oui | Montant HT |
taxAmount |
string | Oui | Montant TVA |
taxBreakdown |
array | Oui | Ventilation TVA |
Codes d'erreur
| Code | Description |
|---|---|
MISSING_TRANSACTIONS |
Transactions requises pour flux 10.3 |
MISSING_INVOICES |
Factures requises pour flux 10.1 |
MISSING_PAYMENTS |
Paiements requis pour flux 10.2/10.4 |
INVALID_FIELD |
Champ incorrect pour le type de flux |
INVALID_SIREN |
Format SIREN/SIRET invalide |
INVALID_CATEGORY |
Code de catégorie invalide (utiliser TLB1, TPS1, TNT1, TMA1) |
TAX_MISMATCH |
Incohérence entre montant TVA et ventilation |
DATE_OUT_OF_PERIOD |
Date de transaction hors période |
DOMESTIC_BUYER |
Acheteur FR (warning pour B2B int.) |
NO_DATA |
Aucune donnée fournie dans le dépôt agrégé |
FlowType AFNOR
Le FlowType est automatiquement déterminé selon le contenu du dépôt :
| Contenu du dépôt | FlowType AFNOR |
|---|---|
| 10.3 seul (B2C agrégé) | AggregatedCustomerTransactionReport |
| 10.1 seul (B2Bi ventes) | UnitaryCustomerTransactionReport |
| 10.4 seul (paiements B2C) | AggregatedCustomerPaymentReport |
| 10.2 seul (paiements B2Bi) | UnitaryCustomerPaymentReport |
| Plusieurs types combinés | MultiFlowReport |
Bonnes pratiques
-
Utilisez les codes PPF conformes : Remplacez
B2CparTLB1ouTPS1,EXEMPTparTNT1 -
Préférez les dépôts agrégés : Utilisez
/generate-aggregatedpour créer un fichier unique contenant tous vos flux -
Spécifiez le transmissionType :
INpour un dépôt initial,REpour une rectification -
Toujours valider avant de soumettre : Utilisez
/validateou/validate-aggregatedpour vérifier vos données -
Utilisez des IDs uniques : Le
reportIddoit être unique pour chaque rapport -
Respectez les dates : Les transactions doivent être dans la période déclarée
-
Vérifiez les totaux TVA : La somme des
taxAmountdanstaxBreakdowndoit égaler letaxAmountglobal -
Conservez les flow_id : Après soumission, gardez le
flowIdpour le suivi du statut
Workflow complet : de la génération au suivi
Étape 1 : Récupérer les codes conformes
# Vérifier les codes de catégorie PPF valides
response = requests.get(
f"{BASE_URL}/ereporting/category-codes",
headers=headers
)
print("Codes PPF:", response.json())
Étape 2 : Valider les données
# Valider avant de soumettre
response = requests.post(
f"{BASE_URL}/ereporting/validate-aggregated",
headers=headers,
json=ereporting_data
)
result = response.json()
if not result["valid"]:
for error in result["errors"]:
print(f"ERREUR: {error['code']}: {error['message']}")
sys.exit(1)
print(f"FlowType déterminé: {result['flowType']}")
Étape 3 : Générer le XML
# Générer le XML pour vérification
response = requests.post(
f"{BASE_URL}/ereporting/generate-aggregated",
headers=headers,
json=ereporting_data
)
result = response.json()
print(f"XML généré: {len(result['xml'])} bytes")
print(f"FlowType: {result['flowType']}")
print(f"Contenu: {result['contentSummary']}")
# Sauvegarder pour vérification
with open(f"preview_{result['reportId']}.xml", "w") as f:
f.write(result["xml"])
Étape 4 : Soumettre à la PA
response = requests.post(
f"{BASE_URL}/ereporting/submit-aggregated",
headers=headers,
json={
**ereporting_data,
"trackingId": f"TRACK-{ereporting_data['reportId']}"
}
)
if response.status_code == 200:
result = response.json()
flow_id = result["flowId"]
print(f"Soumis! Flow ID: {flow_id}")
Étape 5 : Suivre le statut
# Rechercher le flux par son ID
response = requests.post(
f"{BASE_URL}/afnor/flows/search",
headers=headers,
json={"flowId": flow_id}
)
for flow in response.json().get("results", []):
print(f"Statut: {flow['acknowledgmentStatus']}")
Migration depuis l'ancienne API
Si vous utilisiez l'ancienne API avec les champs dépréciés :
| Ancien champ | Nouveau champ | Anciennes valeurs | Nouvelles valeurs |
|---|---|---|---|
category |
categoryCode |
B2C, B2BINT, EXEMPT | TLB1, TPS1, TNT1 |
taxDueType |
taxDueDateTypeCode |
I, S | 5, 29, 72 |
| (absent) | transmissionType |
(aucun) | IN, RE |
Compatibilité arrière : L'API accepte toujours les anciennes valeurs et les convertit automatiquement :
"category": "B2C"→"categoryCode": "TLB1""category": "EXEMPT"→"categoryCode": "TNT1"
Cependant, nous recommandons de migrer vers les nouveaux champs pour une conformité PPF complète.
Besoin d'aide ?
Notre équipe est disponible pour vous accompagner dans l'intégration du e-reporting :
- Documentation OpenAPI : https://factpulse.fr/api/facturation/scalar
- Swagger UI : https://factpulse.fr/api/facturation/openapi.json
- Support : contact@factpulse.fr
- Sandbox : Testez gratuitement l'API en mode bac à sable
Dernière mise à jour : Janvier 2026 - Conformité PPF v3.1