@fiscapi-node/sdk
v1.5.1
Published
SDK officiel Node.js/TypeScript pour l'API FiscAPI - Facturation e-MECeF au Bénin
Maintainers
Readme
FiscAPI SDK pour Node.js
SDK officiel pour l'intégration de la facturation e-MECeF (DGI Bénin) avec FiscAPI.
✨ Nouveautés v1.5.0
- ✅ Création de factures proforma via API v1 et SDK Node.
- ✅ Conversion d'une proforma en facture, avec normalisation optionnelle.
- ✅ PDF proforma disponible via URL signée par clé API.
✨ Nouveautés v1.4.3
- ✅
FISCAPI_LOCAL_BASE_URLredevient disponible comme override optionnel pour les installations FiscAPI on-prem. - ✅ Aucun
baseUrlnitargetà passer au constructeur.
✨ Nouveautés v1.4.2
- 🔧 Retrait de
baseUrl,targetet des variables d'URL de la configuration publique. - ✅ Le SDK utilise directement l'endpoint officiel FiscAPI.
✨ Nouveautés v1.4.1
- 🔧 SDK recentré sur l'usage e-MECeF : factures, crédits, profils fiscaux et webhooks.
- 🔧 Retrait de la ressource IA publiée par erreur dans
1.4.0. - ✅ Pour les actions IA, utilisez
@fiscapi-node/local-sdk.
✨ Nouveautés v1.3.0
- ✅
getPdfUrl(id, { absolute: true })pour obtenir une URL absolue correcte - ✅ Chaîne de tests mise à niveau pour corriger les vulnérabilités connues
✨ Nouveautés v1.2.0
- ✅ Support complet des avoirs (FA/EA)
- ✅ Endpoint de normalisation des brouillons
- ✅ Authentification via header
x-api-key - ✅ Support des réponses paginées
- ✅ Calcul TVA côté client
- ✅ API v2 (compta) : vendor rules, relevés bancaires, uploads multipart
📦 Installation
npm install @fiscapi-node/sdk
# ou
yarn add @fiscapi-node/sdk🔐 Sécurité - IMPORTANT
⚠️ NE JAMAIS exposer votre clé API côté client
// ❌ DANGEREUX - La clé est visible dans le code source
const fiscapi = new FiscApi('sk_live_xxx'); // Dans le navigateur = FUITE✅ Utilisez TOUJOURS le SDK côté serveur
| Architecture | Sécurité | Recommandation | |--------------|----------|----------------| | Node.js / Backend | ✅ Sécurisé | ✅ Recommandé | | Serverless (Vercel, Netlify) | ✅ Sécurisé | ✅ Recommandé | | Frontend (React, Vue, etc.) | ❌ Dangereux | ❌ À éviter | | Mobile (React Native, Flutter) | ❌ Dangereux | ❌ À éviter |
Architecture recommandée
┌─────────────────┐ ┌─────────────────┐ ┌────────────────┐
│ Votre Frontend │ --> │ Votre Backend │ --> │ FiscAPI v1 │
│ (React, Vue) │ │ (Node, Python) │ │ (clé API ici) │
└─────────────────┘ └─────────────────┘ └────────────────┘
▲ │
│ │
Sans clé Clé API en
(session) variable envVariables d'environnement
# .env (jamais commité)
FISCAPI_API_KEY=sk_live_xxx
# Optionnel: seulement si vous utilisez une instance FiscAPI e-MECeF on-prem
FISCAPI_LOCAL_BASE_URL=http://127.0.0.1:3001
# .env.example (à committer)
FISCAPI_API_KEY=
FISCAPI_LOCAL_BASE_URL=// Charger la clé depuis l'environnement
const fiscapi = new FiscApi(process.env.FISCAPI_API_KEY!);Sans FISCAPI_LOCAL_BASE_URL, le SDK utilise automatiquement FiscAPI Cloud: https://www.fiscapi.com.
Avec FISCAPI_LOCAL_BASE_URL, il cible votre instance locale/on-prem.
🚀 Démarrage rapide
import { FiscApi } from '@fiscapi-node/sdk';
// Initialiser avec votre clé API (côté serveur uniquement)
const fiscapi = new FiscApi(process.env.FISCAPI_API_KEY!);
// Créer et normaliser une facture
const invoice = await fiscapi.invoices.create({
type: 'FV', // FV = Facture de Vente
items: [
{
name: 'Consultation',
price: 50000, // Prix TTC
quantity: 1,
taxGroup: 'B' // B = TVA 18%
}
],
client: {
name: 'SARL Example',
ifu: '0101010101010' // Optionnel
}
});
// Récupérer les données de sécurité DGI
console.log(invoice.status); // 'CONFIRMED'
console.log(invoice.security?.codeMECeFDGI); // 'TEST-XXXX-YYYY-ZZZZ'
console.log(invoice.security?.qrCode); // QR code à imprimer
console.log(invoice.security?.nim); // NIM de la machine
console.log(invoice.totals.total); // 50000
console.log(invoice.totals.totalTVA); // 7627Le SDK est un wrapper HTTP : les mêmes endpoints sont disponibles en API directe via
/api/v1/*et/api/v2/*avec le headerx-api-key.
🧱 Structure du SDK
Le client expose des ressources qui correspondent directement aux endpoints HTTP :
fiscapi.invoices→ API v1 (factures, normalisation, avoirs)fiscapi.proformas→ API v1 (proformas, conversion en facture)fiscapi.credits→ API v1 (crédits)fiscapi.fiscalProfiles→ API v1 (profils fiscaux)fiscapi.webhooks→ API v1 (webhooks)fiscapi.taxGroups→ local (pas d'appel HTTP)fiscapi.accounting.*→ API v2 (comptabilité)fiscapi.tax.*→ API v2 (fiscalité)
🧾 API v2 - Comptabilité & fiscalité
Les endpoints compta/fiscalité sont exposés sous /api/v2/* et accessibles via fiscapi.accounting.* / fiscapi.tax.*.
Dépense + justificatif (image) + OCR
import fs from 'node:fs';
const receipt = new Blob([fs.readFileSync('./invoice.jpg')], { type: 'image/jpeg' });
const created = await fiscapi.accounting.expenses.createWithReceipt(
{ vendorName: 'SBEE', amountTtc: 12000, runOcr: true },
receipt,
{ fileName: 'invoice.jpg' },
);
await fiscapi.accounting.expenses.ocr(created.expenseId);Import relevé bancaire
const statementFile = new Blob([fs.readFileSync('./releve.csv')], { type: 'text/csv' });
const statement = await fiscapi.accounting.bankStatements.create(statementFile, {
fileName: 'releve.csv',
label: 'Banque Janvier',
bankAccountCode: '521',
currency: 'XOF',
});📋 Format de requête (Format DGI)
interface CreateInvoicePayload {
// Type de facture
type: 'FV' | 'FA' | 'EV' | 'EA'; // FV=Vente, FA=Avoir, EV=Export, EA=Avoir Export
// AIB (Acompte Impôt Bénéfice)
aib?: 'A' | 'B' | null; // A=1%, B=5%, null=pas d'AIB
// Client (optionnel pour vente au comptoir)
client?: {
name: string; // Nom (min 5 caractères)
ifu?: string; // IFU 13 chiffres
contact?: string; // Téléphone
address?: string; // Adresse
};
// Articles
items: Array<{
name: string; // Désignation
price: number; // Prix unitaire
quantity: number; // Quantité
taxGroup: 'A'|'B'|'C'|'D'|'E'|'F';
taxSpecific?: number; // Taxe spécifique par unité
}>;
// Paiements
payment?: Array<{
name: 'ESPECES' | 'VIREMENT' | 'CARTEBANCAIRE' | 'MOBILEMONEY';
amount: number;
}>;
// Options
pricingMode?: 'HT' | 'TTC'; // Défaut: TTC
autoNormalize?: boolean; // Défaut: true
reference?: string; // Pour avoirs FA/EA
}📊 Groupes de taxes
| Code | Nom | Taux | Description |
|------|-----|------|-------------|
| A | Exonéré | 0% | Produits exonérés de TVA |
| B | Taxable | 18% | TVA standard |
| C | Exportation | 0% | Vente à l'export |
| D | Exception | 0% | Régime d'exception |
| E | TPS | 0% | Taxe Professionnelle Synthétique |
| F | Spécifique | 0% | Taxe spécifique uniquement |
📝 Exemples
Facture simple (FV)
const invoice = await fiscapi.invoices.create({
type: 'FV',
client: { name: 'Client Simple' },
items: [
{ name: 'Consultation', price: 50000, quantity: 1, taxGroup: 'B' }
]
});
console.log(invoice.status); // 'CONFIRMED'
console.log(invoice.security?.codeMECeFDGI); // 'TEST-XXXX-...'Facture multi-lignes
const invoice = await fiscapi.invoices.create({
type: 'FV',
client: {
name: 'Entreprise ABC',
ifu: '0101010101010',
contact: '+229 97 00 00 00',
address: 'Cotonou, Bénin'
},
items: [
{ name: 'Ordinateur portable', price: 350000, quantity: 2, taxGroup: 'B' },
{ name: 'Souris sans fil', price: 15000, quantity: 5, taxGroup: 'B' },
{ name: 'Câble HDMI', price: 5000, quantity: 3, taxGroup: 'A' } // Exonéré
]
});Facture avec AIB
const invoice = await fiscapi.invoices.create({
type: 'FV',
aib: 'A', // 1% du montant
client: {
name: 'Client avec AIB',
ifu: '0202020202020'
},
items: [
{ name: 'Prestation de services', price: 100000, quantity: 1, taxGroup: 'B' }
]
});
console.log(invoice.totals.aib); // Montant AIB (1000 FCFA)Facture d'exportation (EV)
const invoice = await fiscapi.invoices.create({
type: 'EV',
client: {
name: 'Client Export International',
address: 'Paris, France'
},
items: [
{ name: 'Noix de cajou (export)', price: 500000, quantity: 10, taxGroup: 'C' },
{ name: 'Karité brut (export)', price: 250000, quantity: 5, taxGroup: 'C' }
]
});
console.log(invoice.totals.totalTVA); // 0 (pas de TVA à l'export)Mode HT (prix hors taxes)
const invoice = await fiscapi.invoices.create({
type: 'FV',
pricingMode: 'HT', // Prix hors taxes
client: { name: 'Client HT' },
items: [
{ name: 'Service', price: 10000, quantity: 1, taxGroup: 'B' }
]
});
// Le SDK calcule automatiquement la TVA
console.log(invoice.totals.totalHT); // 10000
console.log(invoice.totals.totalTVA); // 1800
console.log(invoice.totals.total); // 11800🧾 Proformas
Une proforma créée via l'API n'est pas normalisée DGI. Elle garde le même profil fiscal et le même compte de facturation que la clé API, puis peut être convertie en facture quand le client valide le devis.
const proforma = await fiscapi.proformas.create({
type: 'FV',
validityDays: 15,
client: {
name: 'Client Proforma',
ifu: '0101010101010'
},
items: [
{ name: 'Prestation', price: 125000, quantity: 1, taxGroup: 'B' }
],
payment: [{ name: 'VIREMENT' }],
externalRef: 'DEVIS-2026-001'
});
console.log(proforma.proformaNumber); // PRO-2026-0001
console.log(proforma.computedStatus); // 'pending'
console.log(proforma.pdfUrl); // /api/v1/proformas/{id}/pdf
const invoice = await fiscapi.proformas.convertToInvoice(proforma.uid, {
autoNormalize: false
});
console.log(invoice.internalNumber); // Numéro interne de facturePour convertir une proforma expirée, passez { forceConvert: true }. Pour créer la facture et lancer directement la normalisation DGI, passez { autoNormalize: true }.
Supprimer une proforma non convertie
Depuis @fiscapi-node/[email protected], un système externe comme HERMES peut supprimer une proforma encore en attente :
const deleted = await fiscapi.proformas.delete(proforma.uid);
console.log(deleted.success); // true
console.log(deleted.proformaNumber); // PRO-2026-0001La suppression est volontairement refusée si la proforma a déjà été convertie en facture, car la facture générée devient le document métier à suivre. Dans ce cas l'API renvoie 409 avec le code PROFORMA_ALREADY_CONVERTED et invoiceId.
La clé API doit avoir le scope INVOICE_WRITE. La suppression reste strictement limitée au tenant propriétaire de la clé.
💳 Avoirs (Remboursements)
Avoir total (remboursement complet)
// Créer d'abord une facture
const invoice = await fiscapi.invoices.create({
type: 'FV',
client: { name: 'Client à rembourser' },
items: [
{ name: 'Produit', price: 30000, quantity: 1, taxGroup: 'B' }
]
});
// Attendre 4 secondes pour la prise en compte DGI
await new Promise(resolve => setTimeout(resolve, 4000));
// Créer l'avoir total
const creditNote = await fiscapi.invoices.refund(invoice.uid, 'Annulation commande');
console.log(creditNote.type); // 'FA'
console.log(creditNote.status); // 'CONFIRMED'
console.log(creditNote.isCreditNote); // true
console.log(creditNote.creditNote?.originalInvoiceUid); // Facture originale
console.log(creditNote.security?.codeMECeFDGI); // Code de l'avoirAvoir partiel (remboursement sélectif)
// Rembourser 2 unités de la ligne 1
const creditNote = await fiscapi.invoices.refundPartial('clx123abc', {
lines: [
{ position: 1, quantity: 2 }, // Ligne 1, 2 unités
{ position: 3 }, // Ligne 3 entière
],
reason: 'Retour produit défectueux'
});Détecter les avoirs et annulations dans vos systèmes
const { data: invoices } = await fiscapi.invoices.list({ limit: 100 });
for (const invoice of invoices) {
if (invoice.isCreditNote || invoice.type === 'FA' || invoice.type === 'EA') {
console.log('Avoir détecté', invoice.uid, invoice.creditNote?.originalInvoiceUid);
}
if (invoice.status === 'CANCELLED' || invoice.cancellation?.isCancelled) {
console.log('Facture annulée', invoice.uid, invoice.cancellation?.creditNotes);
}
}📄 Brouillons
Créer un brouillon (sans normaliser)
const draft = await fiscapi.invoices.createDraft({
type: 'FV',
items: [{ name: 'Test', price: 25000, quantity: 2, taxGroup: 'B' }],
client: { name: 'Client Draft' }
});
console.log(draft.status); // 'DRAFT'Normaliser le brouillon plus tard
const confirmed = await fiscapi.invoices.normalize(draft.uid);
console.log(confirmed.status); // 'CONFIRMED'
console.log(confirmed.security?.codeMECeFDGI); // Code MECeF⚠️ Gestion des erreurs
import { FiscApiError } from '@fiscapi-node/sdk';
try {
const invoice = await fiscapi.invoices.create({ ... });
} catch (error) {
if (error instanceof FiscApiError) {
console.error('Code:', error.code); // 'VALIDATION_ERROR'
console.error('Message:', error.message);
console.error('Status:', error.status); // 422
console.error('Details:', error.details);
// Codes d'erreur possibles
switch (error.code) {
case 'VALIDATION_ERROR':
// Payload invalide
break;
case 'INSUFFICIENT_CREDITS':
// Pas assez de crédits
break;
case 'NORMALIZATION_ERROR':
// Erreur e-MCF (rejet DGI)
break;
case 'UNAUTHORIZED':
// Clé API invalide
break;
}
}
}🛠 Utilitaires
Calculer la TVA (synchrone)
// Calculer à partir du TTC
const result = fiscapi.taxGroups.calculateVat(11800, 'B', 'TTC');
console.log(result); // { ht: 10000, tax: 1800, ttc: 11800 }
// Calculer à partir du HT
const result2 = fiscapi.taxGroups.calculateVat(10000, 'B', 'HT');
console.log(result2); // { ht: 10000, tax: 1800, ttc: 11800 }
// Exonéré
const result3 = fiscapi.taxGroups.calculateVat(50000, 'A', 'TTC');
console.log(result3); // { ht: 50000, tax: 0, ttc: 50000 }Vérifier les crédits
const balance = await fiscapi.credits.getBalance();
console.log(`Crédits: ${balance.available}/${balance.total}`);
if (balance.isLow) {
console.warn('Solde bas ! Rechargez vos crédits.');
}
// Vérifier avant un batch
const canProcess = await fiscapi.credits.hasSufficientBalance(100);Vérifier la connexion
const isHealthy = await fiscapi.ping();
if (!isHealthy) {
console.error('API FiscAPI indisponible');
}Lister les factures avec pagination
const { data: invoices, count } = await fiscapi.invoices.list({
limit: 20,
status: 'CONFIRMED'
});
console.log(`${count} factures trouvées`);
invoices.forEach(inv => console.log(inv.uid, inv.status));⚙️ Configuration avancée
const fiscapi = new FiscApi({
apiKey: process.env.FISCAPI_API_KEY!,
timeout: 30000, // Optionnel (ms)
});🔄 Polling (attente de confirmation)
// Attendre qu'une facture soit confirmée
const confirmed = await fiscapi.invoices.waitForConfirmation('clx123abc', {
maxAttempts: 30, // Défaut: 30
intervalMs: 1000, // Défaut: 1000ms
});📚 API Reference
fiscapi.invoices
| Méthode | Description |
|---------|-------------|
| create(payload) | Créer et normaliser une facture |
| createDraft(payload) | Créer un brouillon |
| get(id) | Récupérer une facture |
| list(filters?) | Lister les factures (paginé) |
| normalize(id) | Normaliser un brouillon |
| refund(id, reason?) | Avoir total |
| refundPartial(id, options) | Avoir partiel |
| isConfirmed(id) | Vérifier si confirmée |
| waitForConfirmation(id, options?) | Polling |
| getPdfUrl(id, options?) | URL du PDF |
fiscapi.proformas
| Méthode | Description |
|---------|-------------|
| create(payload) | Créer une proforma sans normalisation DGI |
| get(id) | Récupérer une proforma |
| list(filters?) | Lister les proformas |
| convertToInvoice(id, options?) | Convertir en facture |
| delete(id) | Supprimer une proforma non convertie |
| getPdfUrl(id, options?) | URL du PDF proforma |
fiscapi.credits
| Méthode | Description |
|---------|-------------|
| getBalance() | Solde de crédits |
| hasSufficientBalance(n) | Vérifier solde suffisant |
| listPacks() | Packs disponibles |
fiscapi.fiscalProfiles
| Méthode | Description |
|---------|-------------|
| list() | Lister les profils |
| get(id) | Récupérer un profil |
| hasValidToken(id) | Token e-MCF valide ? |
fiscapi.taxGroups
| Méthode | Description |
|---------|-------------|
| list() | Tous les groupes |
| get(code) | Infos d'un groupe |
| calculateVat(amount, taxGroup, mode) | Calculer TVA |
🔁 Mapping SDK ↔ Endpoints HTTP
API v1 (facturation, crédits, webhooks)
| SDK | HTTP |
| --- | --- |
| fiscapi.invoices.create(payload) | POST /api/v1/invoices |
| fiscapi.invoices.createDraft(payload) | POST /api/v1/invoices (autoNormalize=false) |
| fiscapi.invoices.get(id) | GET /api/v1/invoices/{id} |
| fiscapi.invoices.list(filters) | GET /api/v1/invoices |
| fiscapi.invoices.normalize(id) | POST /api/v1/invoices/{id}/normalize |
| fiscapi.invoices.refund(id, reason?) | POST /api/v1/invoices/{id}/credit-note (mode=TOTAL) |
| fiscapi.invoices.refundPartial(id, opts) | POST /api/v1/invoices/{id}/credit-note (mode=PARTIAL) |
| fiscapi.invoices.getPdfUrl(id, { absolute: true }) | GET /api/v1/invoices/{id}/pdf |
| fiscapi.proformas.create(payload) | POST /api/v1/proformas |
| fiscapi.proformas.get(id) | GET /api/v1/proformas/{id} |
| fiscapi.proformas.list(filters) | GET /api/v1/proformas |
| fiscapi.proformas.convertToInvoice(id, opts) | POST /api/v1/proformas/{id}/convert |
| fiscapi.proformas.delete(id) | DELETE /api/v1/proformas/{id} |
| fiscapi.proformas.getPdfUrl(id, { absolute: true }) | GET /api/v1/proformas/{id}/pdf |
| fiscapi.credits.getBalance() | GET /api/v1/credits/balance |
| fiscapi.credits.listPacks() | GET /api/v1/credits/packs |
| fiscapi.fiscalProfiles.list() | GET /api/v1/fiscal-profiles |
| fiscapi.fiscalProfiles.get(id) | GET /api/v1/fiscal-profiles/{id} |
| fiscapi.webhooks.create(config) | POST /api/v1/webhooks |
| fiscapi.webhooks.list() | GET /api/v1/webhooks |
| fiscapi.webhooks.get(id) | GET /api/v1/webhooks/{id} |
| fiscapi.webhooks.update(id, patch) | PATCH /api/v1/webhooks/{id} |
| fiscapi.webhooks.delete(id) | DELETE /api/v1/webhooks/{id} |
| fiscapi.webhooks.rotateSecret(id) | POST /api/v1/webhooks/{id}/rotate-secret |
| fiscapi.taxGroups.* | local (pas d'appel HTTP) |
API v2 (comptabilité & fiscalité)
| SDK | HTTP |
| --- | --- |
| fiscapi.accounting.journal.list(filters) | GET /api/v2/accounting/journal |
| fiscapi.accounting.journal.get(id) | GET /api/v2/accounting/journal/{id} |
| fiscapi.accounting.journal.create(payload) | POST /api/v2/accounting/journal |
| fiscapi.accounting.journal.post(id) | POST /api/v2/accounting/journal/{id}/post |
| fiscapi.accounting.vendors.list(filters) | GET /api/v2/accounting/vendors |
| fiscapi.accounting.vendors.upsert(payload) | POST /api/v2/accounting/vendors |
| fiscapi.accounting.expenses.list(filters) | GET /api/v2/accounting/expenses |
| fiscapi.accounting.expenses.create(payload) | POST /api/v2/accounting/expenses |
| fiscapi.accounting.expenses.createWithReceipt(payload, file) | POST /api/v2/accounting/expenses (multipart) |
| fiscapi.accounting.expenses.get(id) | GET /api/v2/accounting/expenses/{id} |
| fiscapi.accounting.expenses.update(id, payload) | PUT /api/v2/accounting/expenses/{id} |
| fiscapi.accounting.expenses.delete(id) | DELETE /api/v2/accounting/expenses/{id} |
| fiscapi.accounting.expenses.ocr(id) | POST /api/v2/accounting/expenses/{id}/ocr |
| fiscapi.accounting.expenses.recognize(id) | POST /api/v2/accounting/expenses/{id}/recognize |
| fiscapi.accounting.expenses.pay(id, payload) | POST /api/v2/accounting/expenses/{id}/payments |
| fiscapi.accounting.expenses.uploadDocument(id, file) | POST /api/v2/accounting/expenses/{id}/documents (multipart) |
| fiscapi.accounting.documents.getSignedUrl(id) | GET /api/v2/accounting/documents/{id}/signed-url |
| fiscapi.accounting.vendorRules.list(filters) | GET /api/v2/accounting/vendor-rules |
| fiscapi.accounting.vendorRules.create(payload) | POST /api/v2/accounting/vendor-rules |
| fiscapi.accounting.vendorRules.update(id, payload) | PUT /api/v2/accounting/vendor-rules/{id} |
| fiscapi.accounting.vendorRules.delete(id) | DELETE /api/v2/accounting/vendor-rules/{id} |
| fiscapi.accounting.bankStatements.list(filters) | GET /api/v2/accounting/bank-statements |
| fiscapi.accounting.bankStatements.create(file, payload) | POST /api/v2/accounting/bank-statements (multipart) |
| fiscapi.accounting.bankStatements.get(id) | GET /api/v2/accounting/bank-statements/{id} |
| fiscapi.accounting.bankStatements.getSignedUrl(id) | GET /api/v2/accounting/bank-statements/{id}/signed-url |
| fiscapi.accounting.bankStatements.getSuggestions(id, params) | GET /api/v2/accounting/bank-statements/{id}/suggestions |
| fiscapi.accounting.bankStatements.createMatch(id, payload) | POST /api/v2/accounting/bank-statements/{id}/matches |
| fiscapi.accounting.bankStatements.deleteMatch(id, params) | DELETE /api/v2/accounting/bank-statements/{id}/matches |
| fiscapi.tax.obligations.list(filters) | GET /api/v2/tax/obligations |
| fiscapi.tax.obligations.upsert(payload) | POST /api/v2/tax/obligations |
| fiscapi.tax.paymentOrders.list(filters) | GET /api/v2/tax/payment-orders |
| fiscapi.tax.paymentOrders.create(payload) | POST /api/v2/tax/payment-orders |
🧪 Tests effectués
Le SDK a été testé avec succès pour :
- ✅ Facture de Vente simple (FV)
- ✅ Facture multi-lignes avec TVA mixte
- ✅ Facture avec AIB (1% et 5%)
- ✅ Facture d'Exportation (EV)
- ✅ Création et normalisation de brouillons
- ✅ Avoir total (FA) avec code MECeF
- ✅ Mode de tarification HT et TTC
- ✅ Calculs TVA synchrones
🆘 Support
- 📖 Documentation: https://fiscapi.com/developers
- 📧 Email: [email protected]
- 🖥️ Dashboard: https://fiscapi.com/dashboard
📄 Licence
MIT
