---
title: "Envoi de fichiers"
description: "Envoyer les fichiers de factures, d'avoirs et de pièces jointes d'email, en binaire ou en base64."
canonical: https://developer.billabex.com/fr/guides/televersement-de-fichiers/
lang: fr
alternate: https://developer.billabex.com/en/guides/file-uploads/
last-updated: 2026-10-06
---

# Envoi de fichiers

> Envoyer les fichiers de factures, d'avoirs et de pièces jointes d'email, en binaire ou en base64.

Source: https://developer.billabex.com/fr/guides/televersement-de-fichiers/
Langue: Français (fr)
Version anglaise: https://developer.billabex.com/en/guides/file-uploads/

Créer une facture ou un avoir par l'API Billabex suppose de joindre un document, PDF ou image. Ce guide décrit les formats acceptés, les limites de taille et les modes d'encodage.

## Vue d'ensemble

Un fichier est exigé par :

- `POST /api/public/v1/invoices` : créer une facture
- `POST /api/public/v1/credit-notes` : créer un avoir
- `PUT /api/public/v1/organizations/{organizationId}/logo` : poser le logo de l'organisation
- `PUT /api/public/v1/users/me/avatar` : poser la photo de profil de l'utilisateur authentifié

Ces endpoints acceptent des requêtes `multipart/form-data` avec un fichier joint.

Les pièces jointes d'email prennent une autre forme : elles voyagent en JSON et non en
`multipart/form-data`, parce qu'un email en porte plusieurs à la fois. Les deux endpoints ci-dessous
acceptent un tableau `files` d'objets `{ content, filename, mimeType }`, où `content` est le fichier
encodé en base64, aux côtés d'un tableau `attachmentIds`/`attachments` nommant des fichiers qui
existent déjà :

- `POST /api/public/v1/accounts/{accountId}/outgoing-email-communications` : envoyer un email tout de suite
- `PUT /api/public/v1/outgoing-email-communications/{communicationId}` : modifier une relance planifiée

À l'envoi comme à la modification, chaque fichier existant doit appartenir à l'organisation du
compte. Connaître son identifiant, ou le placer dans une tâche ou un ancien email, ne suffit pas.
Le serveur vérifie l'appartenance avant d'envoyer ; si elle ne peut pas être prouvée pour un ancien
fichier, ajoutez-le à nouveau via `files`. Le MCP suit la même règle. Les fichiers nouveaux sont
attribués à l'organisation après vérification de l'accès au compte ou à la communication.
Les nouveaux documents financiers portent aussi cette preuve, ce qui permet de les joindre dès
leur création. Lors d'un envoi, une erreur tardive peut survenir après l'enregistrement du message :
ses fichiers sont alors conservés pour préserver ses références.

À la modification, `attachments` est la liste des identifiants de pièces à **conserver** : envoyer
une liste plus courte est donc la façon de détacher une pièce. Elle n'accepte que des identifiants
sélectionnables sur cette communication, sous réserve de cette vérification d'appartenance : les fichiers déjà présents sur l'email, ceux
téléversés par `files` dans le même appel, ceux de son manifeste financier, son document de
coordonnées bancaires et, pour un email préparé depuis une tâche, les fichiers de cette tâche : ceux
de son fil et des emails auxquels elle se rapporte. Un document financier que la relance a déclaré
obligatoire ne peut pas être retiré, et `requiredAttachmentIds` sur la communication dit lesquels le
sont. Les fichiers envoyés par `files` sont attachés que `attachments` les cite ou non : leurs
identifiants sont justement créés par cet appel. Le serveur vérifie l'accès à la relance planifiée
avant le téléversement, et supprime tous les fichiers créés par la requête si la modification est
refusée.

Les documents créés ainsi portent le canal `PublicApi`, renvoyé sur chaque facture et chaque avoir. Ils coexistent avec les documents importés par un connecteur et ceux saisis dans la webapp : une organisation peut utiliser tout cela en même temps. Voyez [Sources de compte](/fr/guides/sources-de-compte/) pour la différence entre le `channel` d'un document et la `source` d'un compte.

## Formats acceptés

| Type MIME         | Extension   | Description                |
| ----------------- | ----------- | -------------------------- |
| `application/pdf` | .pdf        | Documents PDF (recommandé) |
| `image/png`       | .png        | Images PNG                 |
| `image/jpeg`      | .jpg, .jpeg | Images JPEG                |
| `image/webp`      | .webp       | Images WebP                |

Le logo d'organisation et l'avatar utilisateur font exception : ils n'acceptent que des images
(`image/png`, `image/jpeg`, `image/webp`), jamais un PDF, et sont plafonnés à 2 Mo. Un `DELETE` sur
la même route retire l'image. Retirer le logo d'une organisation fait revenir au logo déduit de son
domaine email ; retirer un avatar fait revenir aux initiales de l'utilisateur.

## Limites de taille

- **Taille maximale** : 10 Mo une fois décodé, 2 Mo pour les logos et les avatars
- Au-delà, le fichier est rejeté avec une erreur `400 Bad Request`

## Modes d'encodage

Billabex accepte deux encodages :

| Encodage | Content-Transfer-Encoding  | Quand l'utiliser                             |
| -------- | -------------------------- | -------------------------------------------- |
| Binaire  | `7bit`, `8bit` ou `binary` | Par défaut, pour la plupart des clients HTTP |
| Base64   | `base64`                   | Systèmes historiques, NetSuite, etc.         |

### Envoi binaire (par défaut)

Le champ `taxAmount` accepte une TVA signée sur les factures et les avoirs. Par exemple,
`totalAmount=27` et `taxAmount=-31.67` donnent un HT de 58,67 € sans ajustement.
Conservez le signe de la TVA : une déduction de provision taxée peut rendre la TVA nette
négative malgré un TTC positif. La même règle s'applique aux imports MCP et à la webapp.

Pour une facture, `adjustmentAmount` accepte un montant signé après taxe, déjà compris dans
`totalAmount`, et vaut zéro lorsqu'il est absent. Par exemple : 100 € HT, 20 € de TVA et -115 €
d'ajustement donnent `totalAmount=5`, `taxAmount=20`, `adjustmentAmount=-115`. Le détail retourne
ces trois montants ; le solde à régler repose sur le TTC final de 5 €.

La plupart des clients HTTP envoient les fichiers en binaire par défaut. C'est la méthode recommandée quand votre plateforme le permet.

#### Exemple JavaScript

```javascript
const formData = new FormData();
formData.append('accountId', 'ACCOUNT_ID');
formData.append('number', 'INV-2024-001');
formData.append('issuedDate.year', '2024');
formData.append('issuedDate.month', '1');
formData.append('issuedDate.day', '15');
formData.append('dueDate.year', '2024');
formData.append('dueDate.month', '2');
formData.append('dueDate.day', '15');
formData.append('totalAmount', '1500.00');
formData.append('taxAmount', '300.00');
formData.append('paidAmount', '0');
formData.append('billingAddress.street', '123 rue Principale');
formData.append('billingAddress.city', 'Paris');
formData.append('billingAddress.postalCode', '75001');
formData.append('billingAddress.country', 'FR');
formData.append('file', fileBlob, 'facture.pdf');

const response = await fetch('https://next.billabex.com/api/public/v1/invoices', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${accessToken}`,
  },
  body: formData,
});
```

#### Exemple cURL

```bash
curl -X POST "https://next.billabex.com/api/public/v1/invoices" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -F "accountId=ACCOUNT_ID" \
  -F "number=INV-2024-001" \
  -F "issuedDate.year=2024" \
  -F "issuedDate.month=1" \
  -F "issuedDate.day=15" \
  -F "dueDate.year=2024" \
  -F "dueDate.month=2" \
  -F "dueDate.day=15" \
  -F "totalAmount=1500.00" \
  -F "taxAmount=300.00" \
  -F "paidAmount=0" \
  -F "billingAddress.street=123 rue Principale" \
  -F "billingAddress.city=Paris" \
  -F "billingAddress.postalCode=75001" \
  -F "billingAddress.country=FR" \
  -F "file=@facture.pdf"
```

### Envoi en base64

Certaines plateformes, NetSuite par exemple, ne savent pas envoyer de binaire et doivent encoder les fichiers en base64. Billabex détecte et décode automatiquement ce cas.

Pour l'utiliser :

1. encodez le contenu du fichier en base64 ;
2. posez l'en-tête `Content-Transfer-Encoding: base64` sur la partie du fichier.

#### Ce que fait la base64

L'encodage base64 convertit du binaire en texte ASCII, ce qui augmente la taille d'environ 33 %. Un fichier de 10 Mo pèse environ 13,3 Mo une fois encodé.

Billabex le gère seul :

- la limite de 10 Mo porte sur la **taille décodée** ;
- un envoi encodé jusqu'à environ 13,7 Mo est accepté ;
- le fichier est décodé côté serveur avant stockage.

#### Exemple JavaScript (base64)

```javascript
// Lire le fichier et l'encoder en base64
const fileBuffer = await file.arrayBuffer();
const base64Content = btoa(String.fromCharCode(...new Uint8Array(fileBuffer)));

// Construire la requête multipart avec le fichier encodé
const boundary = '----WebKitFormBoundary' + Math.random().toString(36).slice(2);

const body = [
  `--${boundary}`,
  'Content-Disposition: form-data; name="accountId"',
  '',
  'ACCOUNT_ID',
  `--${boundary}`,
  'Content-Disposition: form-data; name="number"',
  '',
  'INV-2024-001',
  // ... autres champs ...
  `--${boundary}`,
  'Content-Disposition: form-data; name="file"; filename="facture.pdf"',
  'Content-Type: application/pdf',
  'Content-Transfer-Encoding: base64',
  '',
  base64Content,
  `--${boundary}--`,
].join('\r\n');

const response = await fetch('https://next.billabex.com/api/public/v1/invoices', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'Content-Type': `multipart/form-data; boundary=${boundary}`,
  },
  body: body,
});
```

#### Exemple Node.js (base64)

```javascript
const fs = require('fs');
const path = require('path');

// Lire le fichier et l'encoder en base64
const filePath = './facture.pdf';
const fileContent = fs.readFileSync(filePath);
const base64Content = fileContent.toString('base64');
const filename = path.basename(filePath);

// Construire le corps multipart à la main
const boundary = '----FormBoundary' + Date.now();
const CRLF = '\r\n';

const parts = [
  `--${boundary}`,
  'Content-Disposition: form-data; name="accountId"',
  '',
  'ACCOUNT_ID',
  `--${boundary}`,
  'Content-Disposition: form-data; name="number"',
  '',
  'INV-2024-001',
  `--${boundary}`,
  'Content-Disposition: form-data; name="issuedDate.year"',
  '',
  '2024',
  `--${boundary}`,
  'Content-Disposition: form-data; name="issuedDate.month"',
  '',
  '1',
  `--${boundary}`,
  'Content-Disposition: form-data; name="issuedDate.day"',
  '',
  '15',
  `--${boundary}`,
  'Content-Disposition: form-data; name="dueDate.year"',
  '',
  '2024',
  `--${boundary}`,
  'Content-Disposition: form-data; name="dueDate.month"',
  '',
  '2',
  `--${boundary}`,
  'Content-Disposition: form-data; name="dueDate.day"',
  '',
  '15',
  `--${boundary}`,
  'Content-Disposition: form-data; name="totalAmount"',
  '',
  '1500.00',
  `--${boundary}`,
  'Content-Disposition: form-data; name="taxAmount"',
  '',
  '300.00',
  `--${boundary}`,
  'Content-Disposition: form-data; name="paidAmount"',
  '',
  '0',
  `--${boundary}`,
  'Content-Disposition: form-data; name="billingAddress.street"',
  '',
  '123 rue Principale',
  `--${boundary}`,
  'Content-Disposition: form-data; name="billingAddress.city"',
  '',
  'Paris',
  `--${boundary}`,
  'Content-Disposition: form-data; name="billingAddress.postalCode"',
  '',
  '75001',
  `--${boundary}`,
  'Content-Disposition: form-data; name="billingAddress.country"',
  '',
  'FR',
  `--${boundary}`,
  `Content-Disposition: form-data; name="file"; filename="${filename}"`,
  'Content-Type: application/pdf',
  'Content-Transfer-Encoding: base64',
  '',
  base64Content,
  `--${boundary}--`,
];

const body = parts.join(CRLF);

const response = await fetch('https://next.billabex.com/api/public/v1/invoices', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${accessToken}`,
    'Content-Type': `multipart/form-data; boundary=${boundary}`,
  },
  body: body,
});
```

#### Exemple Python (base64)

```python
import base64
import requests

# Lire et encoder le fichier
with open('facture.pdf', 'rb') as f:
    file_content = f.read()
    base64_content = base64.b64encode(file_content).decode('ascii')

# Construire le corps multipart
boundary = '----PythonFormBoundary'
crlf = '\r\n'

body_parts = [
    f'--{boundary}',
    'Content-Disposition: form-data; name="accountId"',
    '',
    'ACCOUNT_ID',
    f'--{boundary}',
    'Content-Disposition: form-data; name="number"',
    '',
    'INV-2024-001',
    # ... autres champs ...
    f'--{boundary}',
    'Content-Disposition: form-data; name="file"; filename="facture.pdf"',
    'Content-Type: application/pdf',
    'Content-Transfer-Encoding: base64',
    '',
    base64_content,
    f'--{boundary}--',
]

body = crlf.join(body_parts)

response = requests.post(
    'https://next.billabex.com/api/public/v1/invoices',
    headers={
        'Authorization': f'Bearer {access_token}',
        'Content-Type': f'multipart/form-data; boundary={boundary}',
    },
    data=body.encode('utf-8'),
)
```

## Gestion des erreurs

| Erreur                   | Statut HTTP | Description                              |
| ------------------------ | ----------- | ---------------------------------------- |
| Aucun fichier envoyé     | 400         | Le champ `file` manque dans la requête   |
| Fichier trop volumineux  | 400         | Le fichier dépasse 10 Mo une fois décodé |
| Type de fichier invalide | 400         | Type MIME absent de la liste autorisée   |
| Multipart invalide       | 400         | Requête multipart mal formée             |

Exemple de réponse d'erreur :

```json
{
  "statusCode": 400,
  "message": "File too large. Maximum size: 10485760 bytes",
  "error": "Bad Request"
}
```

## Bonnes pratiques

### Choisir le bon encodage

- **Préférez le binaire** dès que possible, il est plus efficace
- **N'utilisez la base64** que si votre plateforme ne sait pas envoyer de binaire

### Valider côté client

Vérifiez la taille et le type avant d'envoyer, pour éviter des appels inutiles :

```javascript
const MAX_SIZE = 10 * 1024 * 1024; // 10 Mo
const ALLOWED_TYPES = [
  'application/pdf',
  'image/png',
  'image/jpeg',
  'image/webp',
];

if (file.size > MAX_SIZE) {
  throw new Error('Fichier trop volumineux');
}

if (!ALLOWED_TYPES.includes(file.type)) {
  throw new Error('Type de fichier invalide');
}
```

### Préférer le PDF

C'est le format recommandé pour un document de facture :

- il préserve la mise en forme d'un appareil à l'autre ;
- il permet l'extraction du texte ;
- il donne des fichiers plus légers sur les documents à dominante textuelle.

## Pour aller plus loin

- **[Démarrage](/fr/guides/demarrage/)** : les bases de l'API et l'authentification
- **[Référence de l'API](/fr/reference-api/)** : documentation complète des endpoints

## Support

Une question sur l'envoi de fichiers ?
Écrivez-nous via le [formulaire de contact](https://www.billabex.com/fr/contactez-nous/).

---

Billabex developer portal. OpenAPI specification: https://developer.billabex.com/openapi.json.
Agent instructions: https://developer.billabex.com/llms.txt. Complete documentation: https://developer.billabex.com/llms-full.txt.
