# Fichiers et médias

> Convertir des documents, des tableurs, des images et de l'audio en texte, et transformer du texte en voix au format MP3.

Deux endpoints utilitaires qui fonctionnent sans conversation : la conversion de fichier en texte transforme un fichier
en texte brut (la même conversion qu'AYETO utilise pour les fichiers envoyés dans les bases de connaissances), et la synthèse
vocale transforme du texte en fichier MP3. Les deux sont payés avec les
[crédits personnels](account.md#credits) de l'utilisateur, ou avec le crédit de l'utilisateur dans une organisation
lorsque la requête contient un `organization_id`.

## Organisations et crédits

Les deux endpoints acceptent un `organization_id` facultatif. La requête s'exécute alors dans cette
organisation, de la même manière qu'une requête de [chat](chat.md#organisations-et-credits) : le
crédit de l'utilisateur dans l'organisation la paie (le solde partagé de l'organisation ou le
budget individuel de l'utilisateur, voir [crédits](account.md#credits)) à la place des crédits
personnels. L'utilisateur de la clé doit être administrateur ou membre de l'organisation ; les invités et
les utilisateurs extérieurs à celle-ci obtiennent `403`. Sans `organization_id`, la requête est débitée des
crédits personnels. Les identifiants des organisations de l'utilisateur sont renvoyés par
[lister les appartenances aux organisations](account.md#lister-les-appartenances-aux-organisations).

## Convertir un fichier en texte

| | |
|---|---|
| Endpoint | `POST /api/v3/data-loader/load` |
| Portée | `ayeto.data_loader` |
| Limite de débit | [par défaut](conventions.md#limites-de-debit) |

Envoyez un fichier en base64 ; la réponse contient son texte. Selon le type de fichier, le
texte est extrait directement ou lu par un modèle d'IA :

| Type de fichier | Types MIME | Obtention du texte |
|---|---|---|
| Texte brut, CSV, Markdown, HTML, code source | tout `text/*`, ainsi que `application/json`, `application/ld+json`, `application/xml`, `application/xhtml+xml`, `application/javascript`, `application/ics` | Décodé tel quel. L'UTF-8 est détecté ; les autres encodages sont devinés. |
| PDF | `application/pdf` | La couche de texte est extraite, précédée de `PDF page: N` pour chaque page. Lorsqu'un PDF contient trop peu de texte (un document numérisé), ses pages sont rendues et lues par un modèle de vision. |
| Word | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` (`.docx`), `application/msword` (`.doc`) | Texte extrait. |
| Excel | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` (`.xlsx`), `application/vnd.ms-excel` (`.xls`) | Une section par feuille. Pour `.xlsx`, les valeurs des cellules avec leurs adresses, les formules et les plages nommées ; pour `.xls`, uniquement les valeurs des cellules. Les classeurs très volumineux sont tronqués après 100 000 cellules non vides, avec une note à la fin. |
| Images | tout `image/*` | Le texte de l'image est lu par un modèle de vision. Lorsque l'image ne contient (presque) pas de texte, le modèle décrit l'image à la place. |
| Audio | `audio/mpeg` (`.mp3`), `audio/wav`, `audio/wave`, `audio/x-wav` (`.wav`) | Transcrit par un modèle de reconnaissance vocale. Les enregistrements longs sont découpés et transcrits par parties. |

Les autres types de fichiers (archives, vidéo, autres formats audio, présentations) ne sont pas
convertis : la requête est refusée avec `422` `unsupported file type '<mime type>'`
indiquant le type sous lequel le fichier a été traité, et rien n'est facturé.

Le type MIME est déterminé, dans cet ordre, à partir du champ `mime_type`, de l'en-tête d'une data
URL dans `data`, et enfin par détection à partir du contenu du fichier. Envoyez le type explicitement
lorsque vous le connaissez, la détection n'est pas fiable pour tous les formats.

L'endpoint n'impose pas de limite de taille propre, mais le fichier entier transite encodé en base64 dans
un seul corps JSON et est converti pendant que la requête est ouverte. Les gros PDF numérisés, les images et
les enregistrements longs prennent du temps ; gardez des fichiers de taille raisonnable et prévoyez un temps
de réponse long.

### Facturation

L'extraction directe du texte (texte, PDF avec couche de texte, Word, Excel) n'utilise aucun modèle d'IA.
La vision (images, PDF numérisés) et la reconnaissance vocale (audio) sont facturées au prix des
modèles utilisés. Le serveur peut aussi être configuré avec un prix minimum par appel ; lorsqu'un
appel coûte moins que ce minimum, la différence est facturée sous forme de frais. Les crédits consommés
par un appel sont renvoyés dans `credits`.

Les résultats de la lecture par vision sont mis en cache pendant 24 heures : convertir à nouveau la même image ou le même PDF
numérisé dans ce délai ne rappelle pas le modèle et ne coûte que les frais minimum,
s'ils sont définis.

La vérification que l'utilisateur (ou, avec `organization_id`, l'organisation) dispose de crédits
a lieu avant la conversion ; voir [crédits](account.md#credits). L'appel est débité à
l'organisation lorsque `organization_id` est défini, voir [organisations](#organisations-et-credits).

### Requête

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `data` | chaîne | Oui | Le fichier, encodé en base64. Une data URL (`data:application/pdf;base64,JVBERi0x...`) est également acceptée. |
| `mime_type` | chaîne | Non | Type MIME du fichier. Prévaut sur le type d'une data URL et sur la détection. |
| `organization_id` | UUID | Non | Exécuter la conversion dans cette organisation et la débiter du crédit de l'utilisateur dans celle-ci, voir [organisations](#organisations-et-credits). L'utilisateur doit être administrateur ou membre (pas invité). Par défaut : aucune (crédits personnels). |

### Réponse

`200 OK` avec :

| Champ | Type | Description |
|---|---|---|
| `content` | chaîne | Le texte extrait. |
| `size` | entier | Pour une entrée texte, la taille de l'entrée en octets ; pour les fichiers convertis, la longueur de `content` en caractères. |
| `mime_type` | chaîne | Le type MIME sous lequel le fichier a été traité. Pour une image dont le texte a été lu, c'est `text/plain` ; pour une image qui a été décrite, il reste le type de l'image. |
| `credits` | nombre | Crédits consommés par l'appel, frais minimum compris. |

La réponse contient aussi les champs `filename`, `url`, `name`, `meta`, `path`,
`extension`, `is_chunk`, `binary` et `binary_data`. Pour cet endpoint, ils sont toujours
vides ou à `false` ; ignorez-les.

### Erreurs

| Statut | `detail` | Cause |
|---|---|---|
| `401` | `API key is invalid` | La clé n'a pas la portée `ayeto.data_loader`, voir [authentification](authentication.md#erreurs-dauthentification). |
| `403` | `User is not a member of the organization` | `organization_id` ne fait pas partie des organisations de l'utilisateur. |
| `403` | `User does not have write permissions in the organization` | L'utilisateur est invité dans l'organisation. |
| `422` | `invalid base64 data` | `data` n'est pas du base64 valide ni une data URL valide. |
| `422` | `invalid encoding` | Un fichier texte n'a pas pu être décodé. |
| `422` | `unsupported file type '<mime type>'` | Le fichier est d'un type qui n'est pas converti (voir le tableau ci-dessus), par exemple `unsupported file type 'application/zip'`. |
| `422` | `not enough user credit` | Les crédits personnels de l'utilisateur sont épuisés. |
| `422` | `not enough organization credit` | Avec `organization_id` : le crédit de l'utilisateur dans l'organisation est épuisé. |
| `422` | erreur de validation | `data` est absent. |
| `500` | `error reading document` | Un PDF numérisé n'a pas pu être rendu. |
| `500` | `Error while processing Word document` | Le fichier Word n'a pas pu être lu. |
| `500` | `Error while processing Excel file` | Le fichier Excel n'a pas pu être lu. |
| `500` | `Error while processing audio file`, `Error while processing WAV audio file` | L'audio n'a pas pu être converti ou transcrit. |

Les erreurs d'authentification, de limite de débit et du serveur sont décrites dans les
[conventions](conventions.md#erreurs).

### Exemple

```bash
curl -X POST "https://ayeto.ai/api/v3/data-loader/load" \
  -H "uni-api-key: $AYETO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"data\": \"$(base64 -w0 invoice.pdf)\", \"mime_type\": \"application/pdf\"}"
```

```json
{
  "content": "PDF page: 1\nInvoice 2026-0412\nNorthwind Trading s.r.o.\nTotal due: 12 400 CZK\n\n",
  "size": 76,
  "filename": "",
  "url": "",
  "name": "",
  "meta": "",
  "path": "",
  "extension": "",
  "is_chunk": false,
  "mime_type": "application/pdf",
  "binary": false,
  "binary_data": "",
  "credits": 0.0
}
```

## Synthèse vocale

| | |
|---|---|
| Endpoint | `POST /api/v3/tts/mp3` |
| Portée | `ayeto.tts` |
| Limite de débit | [par défaut](conventions.md#limites-de-debit) |

Convertit du texte en voix et renvoie un fichier MP3.

Le texte est découpé en phrases (aux sauts de ligne et à `. `) et chaque phrase est
prononcée séparément ; l'audio de toutes les phrases est assemblé en un seul fichier. Une
phrase ne doit pas dépasser 4 096 caractères.

### Facturation

Chaque phrase est facturée selon son nombre de caractères au prix du
modèle de synthèse vocale (voir le type `tts` dans [modèles](models-and-tools.md#types-de-modeles)).
Une phrase prononcée avec la même voix au cours de la dernière heure est réutilisée et n'est pas facturée
à nouveau. La synthèse vocale est payée avec les [crédits personnels](account.md#credits) de l'utilisateur, ou avec
le crédit de l'utilisateur dans une organisation lorsque `organization_id` est défini (voir
[organisations](#organisations-et-credits)). Le solde est vérifié avant la génération de chaque
phrase.

### Requête

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `text` | chaîne | Oui | Le texte à prononcer. |
| `voice` | chaîne | Non | L'une des voix `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer`. Par défaut : la voix par défaut du serveur (`onyx`, sauf modification par les administrateurs). |
| `filename` | chaîne | Non | Nom de fichier pour l'en-tête `Content-Disposition`. Par défaut `audio.mp3`. |
| `organization_id` | UUID | Non | Générer la voix dans cette organisation et la débiter du crédit de l'utilisateur dans celle-ci, voir [organisations](#organisations-et-credits). L'utilisateur doit être administrateur ou membre (pas invité). Par défaut : aucune (crédits personnels). |

### Réponse

`200 OK` avec le fichier MP3 comme corps (`Content-Type: audio/mpeg`) et un
en-tête `Content-Disposition: attachment` contenant le nom du fichier.

### Erreurs

| Statut | `detail` | Cause |
|---|---|---|
| `401` | `API key is invalid` | La clé n'a pas la portée `ayeto.tts`, voir [authentification](authentication.md#erreurs-dauthentification). |
| `403` | `permission denied` | Le compte de l'utilisateur n'est pas autorisé à utiliser la synthèse vocale. |
| `403` | `User is not a member of the organization` | `organization_id` ne fait pas partie des organisations de l'utilisateur. |
| `403` | `User does not have write permissions in the organization` | L'utilisateur est invité dans l'organisation. |
| `422` | erreur de validation | `text` est absent, ou `voice` ne fait pas partie des voix listées. |
| `422` | `not enough user credit` | Les crédits personnels de l'utilisateur sont épuisés. |
| `422` | `not enough organization credit` | Avec `organization_id` : le crédit de l'utilisateur dans l'organisation est épuisé. |
| `500` | `No audio generated` | `text` est vide. |
| `500` | `Failed to generate audio` | La génération de la voix a échoué, par exemple parce qu'une phrase est trop longue. |

Les erreurs d'authentification, de limite de débit et du serveur sont décrites dans les
[conventions](conventions.md#erreurs).

### Exemple

```bash
curl -X POST "https://ayeto.ai/api/v3/tts/mp3" \
  -H "uni-api-key: $AYETO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text": "Your order has shipped. It will arrive on Friday.", "voice": "nova", "filename": "order-update.mp3"}' \
  --output order-update.mp3
```
