> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moustacheai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Documentation API v1

> Cette documentation décrit les points de terminaison (end-points) REST mis à disposition dans l'API v1 de Moustache AI.

> Tous les exemples utilisent des requêtes HTTP classiques. Les en-têtes et corps sont encodés en **JSON UTF-8**.

***

## 1. Authentification

Chaque requête doit contenir l’en-tête :

```http theme={null}
Authorization: Bearer <API_TOKEN_DE_LA_SOCIETE>
```

`API_TOKEN_DE_LA_SOCIETE` correspond au champ `api_token` de la table **company**.

Si l’en-tête est manquant ou invalide, le serveur renvoie **401 Unauthorized**.

***

## 2. Créer / mettre à jour un client

`POST /api/v1/customers`

| Champ               | Type              | Obligatoire | Description                                                                                                             |
| ------------------- | ----------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------- |
| `companyIdentifier` | `string`          | oui         | Identifiant unique du client (propre à **votre** système).                                                              |
| `metadata`          | `object` *(JSON)* | oui         | Méta-données librement définies (nom, email, etc.). Toutes les clefs et valeurs doivent être des chaînes de caractères. |

### Réponses possibles

| Code | Signification                                         |
| ---- | ----------------------------------------------------- |
| 200  | Création ou mise à jour réussie (`{ success: "…" }`). |
| 400  | Paramètres manquants.                                 |
| 401  | Token d’API invalide.                                 |
| 500  | Erreur interne.                                       |

### Exemple de requête

```bash theme={null}
curl -X POST https://api.moustacheai.com/api/v1/customers \
  -H "Authorization: Bearer MON_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "companyIdentifier": "CLIENT_123",
        "metadata": {
          "email": "contact@client.fr",
          "nom": "Société Exemple"
        }
      }'
```

***

## 2bis. Récupérer un client

`GET /api/v1/customers`

Permet de récupérer les informations d'un client existant à partir de son identifiant.

### Paramètres de requête (query string)

| Paramètre           | Type   | Obligatoire | Description                                                                   |
| ------------------- | ------ | ----------- | ----------------------------------------------------------------------------- |
| `companyIdentifier` | string | oui         | Identifiant unique du client (même valeur que pour `POST /api/v1/customers`). |

### Réponses possibles

| Code | Corps JSON                                                                                  |
| ---- | ------------------------------------------------------------------------------------------- |
| 200  | `{ "id": 1, "company_identifier": "CLIENT_123", "data": { "email": "...", "nom": "..." } }` |
| 400  | `{ "error": "Missing companyIdentifier query parameter" }`                                  |
| 401  | `{ "error": "Unauthorized: Unknown token" }`                                                |
| 404  | `{ "error": "Customer not found" }`                                                         |
| 500  | `{ "error": "Internal Server Error" }`                                                      |

### Exemple de requête

```bash theme={null}
curl -X GET 'https://api.moustacheai.com/api/v1/customers?companyIdentifier=CLIENT_123' \
  -H "Authorization: Bearer MON_API_TOKEN"
```

Réponse :

```json theme={null}
{
  "id": 42,
  "company_identifier": "CLIENT_123",
  "data": {
    "email": "contact@client.fr",
    "nom": "Société Exemple"
  }
}
```

***

## 3. Envoyer une question à un agent

`POST /api/v1/responses`

| Champ               | Type      | Obligatoire       | Description                                                                                                                                                                      |
| ------------------- | --------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `companyIdentifier` | `string`  | oui               | Identifiant du client (même valeur que pour `/customers`).                                                                                                                       |
| `agentUUID`         | `string`  | oui               | UUID de l’agent (assistant) qui doit répondre.                                                                                                                                   |
| `question`          | `string`  | oui               | Question de l’utilisateur final.                                                                                                                                                 |
| `stream`            | `boolean` | non *(def: true)* | `true` → réponse en flux (SSE) ; `false` → réponse complète en une fois.                                                                                                         |
| `files`             | `array`   | non               | Fichiers (images) pour la vision. Chaque élément: `{ "url": "...", "type": "image/png", "name": "optionnel" }`. `url` peut être une URL publique HTTPS ou un `data:` URL base64. |

Aucun `uuid` n’est requis : le serveur génère un identifiant de message unique.

Si `files` est fourni, l’assistant doit utiliser un **modèle vision** (vision activée), sinon la requête échoue en **403**.

Les fichiers peuvent être envoyés :

* en **JSON** via `files` (URL publique ou `data:` URL base64) ;
* en **multipart/form-data** via un champ `files` (un ou plusieurs fichiers).

### Réponses (mode *stream*)

* **200 OK** : renvoie un flux texte `text/html; charset=utf-8` dont chaque chunk est une partie de la réponse. Le flux se termine par le marqueur `:EOS:`.
* Autres codes d’erreur : voir tableau ci-dessous.

### Réponses (mode *non stream*)

| Code | Corps JSON                                                                      |
| ---- | ------------------------------------------------------------------------------- |
| 200  | `{ "message": "…", "buttons": [ "Option 1", "Option 2" ] }`                     |
| 400  | `{ "error": "Missing parameters" }`                                             |
| 401  | `{ "error": "Unauthorized: Unknown token" }`                                    |
| 404  | `{ "error": "Unauthorized: Assistant not found for your company" }`             |
| 403  | `{ "error": "Image inputs require a vision-enabled model for this assistant" }` |
| 500  | `{ "error": "Answer not found" }` ou erreur interne.                            |

### Exemple : réponse complète (stream = false)

```bash theme={null}
curl -X POST https://api.moustacheai.com/api/v1/responses \
  -H "Authorization: Bearer MON_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "companyIdentifier": "CLIENT_123",
        "agentUUID": "28ce3b2d-5791-4905-8d66-123456789abc",
        "question": "Quels sont vos horaires d\'ouverture ?",
        "stream": false
      }'
```

Réponse :

```json theme={null}
{
  "message": "Nous sommes ouverts du lundi au vendredi de 9h à 18h.",
  "buttons": null
}
```

### Exemple : question avec fichiers (stream = false)

```bash theme={null}
curl -X POST https://api.moustacheai.com/api/v1/responses \
  -H "Authorization: Bearer MON_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "companyIdentifier": "CLIENT_123",
        "agentUUID": "28ce3b2d-5791-4905-8d66-123456789abc",
        "question": "Peux-tu décrire l’image ?",
        "stream": false,
        "files": [
          {
            "url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...",
            "type": "image/jpeg",
            "name": "produit-1.jpg"
          }
        ]
      }'
```

### Exemple : envoi multipart/form-data

```bash theme={null}
curl -X POST https://api.moustacheai.com/api/v1/responses \
  -H "Authorization: Bearer MON_API_TOKEN" \
  -F "companyIdentifier=CLIENT_123" \
  -F "agentUUID=28ce3b2d-5791-4905-8d66-123456789abc" \
  -F "question=Peux-tu décrire l’image ?" \
  -F "stream=false" \
  -F "files=@/chemin/vers/produit-1.jpg"
```

### Exemple : plusieurs fichiers (multipart/form-data)

```bash theme={null}
curl -X POST https://api.moustacheai.com/api/v1/responses \
  -H "Authorization: Bearer MON_API_TOKEN" \
  -F "companyIdentifier=CLIENT_123" \
  -F "agentUUID=28ce3b2d-5791-4905-8d66-123456789abc" \
  -F "question=Compare ces deux images" \
  -F "stream=false" \
  -F "files=@/chemin/vers/produit-1.jpg" \
  -F "files=@/chemin/vers/produit-2.jpg"
```

### Exemple : réponse en flux (*stream = true*)

```bash theme={null}
curl -N -X POST https://api.moustacheai.com/api/v1/responses \
  -H "Authorization: Bearer MON_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "companyIdentifier": "CLIENT_123",
        "agentUUID": "28ce3b2d-5791-4905-8d66-123456789abc",
        "question": "Je souhaite prendre rendez-vous.",
        "stream": true
      }'
```

Le flux affiche progressivement la réponse puis se termine par `:EOS:`.

***

## 3bis. Récupérer l'historique d'une conversation

`GET /api/v1/responses`

Permet de récupérer l'historique complet (questions/réponses) d'une conversation entre un client et un agent, au format JSON.

### Paramètres de requête (query string)

| Paramètre           | Type   | Obligatoire | Description                                                |
| ------------------- | ------ | ----------- | ---------------------------------------------------------- |
| `companyIdentifier` | string | oui         | Identifiant du client (même valeur que pour `/customers`). |
| `agentUUID`         | string | oui         | UUID de l’agent (assistant) dont on veut l’historique.     |

L’authentification se fait via le header :

```http theme={null}
Authorization: Bearer <API_TOKEN_DE_LA_SOCIETE>
```

### Réponse

| Code | Corps JSON                                                                                                            |
| ---- | --------------------------------------------------------------------------------------------------------------------- |
| 200  | `{ "messages": [ { "message": "…", "buttons": ["Option 1"], "message_type": "Question", "created_at": "…" }, ... ] }` |
| 400  | `{ "error": "Missing parameters" }`                                                                                   |
| 401  | `{ "error": "Unauthorized: Unknown token" }`                                                                          |
| 404  | `{ "error": "Unauthorized: Assistant not found for your company" }` ou `Customer not found`                           |
| 500  | `{ "error": "Internal Server Error" }`                                                                                |

Chaque message du tableau contient :

* `message` : texte de la question ou réponse
* `buttons` : tableau d’options (si présent)
* `message_type` : "Question" ou "Answer"
* `created_at` : date ISO

### Exemple de requête

```bash theme={null}
curl -X GET 'https://api.moustacheai.com/api/v1/responses?companyIdentifier=CLIENT_123&agentUUID=28ce3b2d-5791-4905-8d66-123456789abc' \
  -H "Authorization: Bearer MON_API_TOKEN"
```

Réponse :

```json theme={null}
{
  "messages": [
    {
      "message": "Bonjour, comment puis-je vous aider ?",
      "buttons": null,
      "message_type": "Question",
      "created_at": "2024-06-07T12:34:56.000Z"
    },
    {
      "message": "Je souhaite prendre rendez-vous.",
      "buttons": null,
      "message_type": "Answer",
      "created_at": "2024-06-07T12:35:10.000Z"
    }
  ]
}
```

***

## 3ter. Alimenter la base documentaire d’un agent

Ces points de terminaison permettent à une intégration externe (par exemple un **workflow n8n**) d’envoyer, lister et supprimer les documents d’un agent. L’API utilise automatiquement le RAG interne ou le Vector Store OpenAI/Azure configuré sur l’agent.

> L’agent est désigné par son UUID dans l’URL. L’`agentUUID` doit appartenir à la société propriétaire du token.

### POST — Ajouter (ou mettre à jour) un document

`POST /api/v1/agents/{agentUUID}/rag-resources`

Envoi en **`multipart/form-data`**.

| Champ        | Type    | Obligatoire | Description                                                                                                                                                                         |
| ------------ | ------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `file`       | fichier | oui         | Le document. Types acceptés : **TXT, MD, CSV, JSON, PDF, DOCX, PPTX, XLSX, MP3, WAV, MP4, SRT, VTT**. Taille max : 500 Mo (configurable).                                           |
| `externalId` | string  | non         | Pour le RAG interne, renvoyer le même `externalId` remplace la version précédente. Pour un Vector Store, le remplacement est automatique lorsqu’un fichier du même nom existe déjà. |

> ℹ️ Pour un agent OpenAI/Azure, le champ `id` retourné est l’identifiant du fichier du Vector Store. Il sert également à le supprimer avec la route DELETE.

### Réponses possibles

| Code | Corps JSON                                                                                                                                                                                                                                                                                     |
| ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 201  | `{ "id": 123, "external_id": "notion-page-42", "file_name": "guide.pdf", "file_type": "application/pdf", "file_size_bytes": 20480, "status": "PENDING", "chunk_count": null, "error_message": null, "created_at": "…", "updated_at": "…" }` (`id` est une chaîne pour un fichier Vector Store) |
| 400  | `{ "error": "No file provided" }` / type ou `externalId` invalide                                                                                                                                                                                                                              |
| 401  | `{ "error": "Provide Auth via Authorization with \"Bearer YOUR_MOUSTACHE_AI_API_TOKEN\"" }`                                                                                                                                                                                                    |
| 404  | `{ "error": "Agent not found for your company" }`                                                                                                                                                                                                                                              |
| 409  | `{ "error": "A concurrent upload for this externalId is in progress; retry shortly" }`                                                                                                                                                                                                         |
| 413  | `{ "error": "File size exceeds the 500 MB limit" }`                                                                                                                                                                                                                                            |
| 422  | Fournisseur actuel incompatible avec les documents                                                                                                                                                                                                                                             |
| 429  | `{ "error": "Rate limit exceeded" }` (en-tête `Retry-After`)                                                                                                                                                                                                                                   |
| 500  | `{ "error": "Failed to upload RAG resource" }`                                                                                                                                                                                                                                                 |

### Exemple

```bash theme={null}
curl -X POST 'https://api.moustacheai.com/api/v1/agents/28ce3b2d-5791-4905-8d66-123456789abc/rag-resources' \
  -H "Authorization: Bearer MON_API_TOKEN" \
  -F "file=@guide.pdf" \
  -F "externalId=notion-page-42"
```

### GET — Lister les documents et leur statut

`GET /api/v1/agents/{agentUUID}/rag-resources`

Renvoie le tableau des documents de l’agent, depuis son RAG interne ou son Vector Store, triés du plus récent au plus ancien (résultats **paginés**). À utiliser pour suivre l’ingestion (`status` passe de `PENDING` → `PROCESSING` → `READY`, ou `FAILED`).

### Paramètres de requête (query string)

| Paramètre    | Type    | Obligatoire | Description                                                                        |
| ------------ | ------- | ----------- | ---------------------------------------------------------------------------------- |
| `externalId` | string  | non         | Ne renvoyer que le document portant cet identifiant externe.                       |
| `status`     | string  | non         | Filtrer par statut : `PENDING`, `PROCESSING`, `READY` ou `FAILED`.                 |
| `limit`      | integer | non         | Taille de page. Défaut **100**, plafonné à **200**.                                |
| `offset`     | integer | non         | Décalage pour la pagination (défaut 0). Combinez avec `limit` pour parcourir tout. |

```bash theme={null}
curl -X GET 'https://api.moustacheai.com/api/v1/agents/28ce3b2d-5791-4905-8d66-123456789abc/rag-resources?externalId=notion-page-42' \
  -H "Authorization: Bearer MON_API_TOKEN"
```

Réponse :

```json theme={null}
[
  {
    "id": 123,
    "external_id": "notion-page-42",
    "file_name": "guide.pdf",
    "file_type": "application/pdf",
    "file_size_bytes": 20480,
    "status": "READY",
    "chunk_count": 18,
    "error_message": null,
    "created_at": "2026-06-18T09:00:00.000Z",
    "updated_at": "2026-06-18T09:01:12.000Z"
  }
]
```

### DELETE — Supprimer un document par clé externe

`DELETE /api/v1/agents/{agentUUID}/rag-resources/{externalId}`

Supprime le document (et ses vecteurs). Pour le RAG interne, utilisez son `externalId`. Pour un Vector Store, utilisez l’`id` de fichier retourné par POST ou GET.

| Code | Signification                                        |
| ---- | ---------------------------------------------------- |
| 204  | Supprimé (pas de corps).                             |
| 401  | Token d’API invalide.                                |
| 404  | `{ "error": "Resource not found" }` / agent inconnu. |
| 500  | `{ "error": "Failed to delete RAG resource" }`       |

```bash theme={null}
curl -X DELETE 'https://api.moustacheai.com/api/v1/agents/28ce3b2d-5791-4905-8d66-123456789abc/rag-resources/notion-page-42' \
  -H "Authorization: Bearer MON_API_TOKEN"
```

***

## 4. Codes d’erreur communs

| Code | Motif                                    |
| ---- | ---------------------------------------- |
| 400  | Paramètre manquant ou invalide           |
| 401  | En-tête `Authorization` absent / mauvais |
| 404  | Agent introuvable pour la société        |
| 500  | Erreur interne inattendue                |

***

## 5. Bonnes pratiques

1. **Timeout côté client** : pour le mode *stream*, coupez la connexion si aucun chunk n’est reçu depuis >20 s.
2. **Réessais** : en cas de **500** réessayez avec back-off exponentiel.
3. **Sécurité** : stockez l’`API_TOKEN` côté serveur uniquement.

***

## 6. Détails techniques du mode *stream*

Le mode streaming (`stream: true`, valeur par défaut) utilise un `ReadableStream` HTTP côté serveur.

### Format

* **Content-Type** : `text/html; charset=utf-8`
* Chaque *chunk* contient un fragment de texte de la réponse de l'agent, encodé en UTF-8.
* Le flux se termine par le marqueur **`:EOS:`** (End Of Stream).
* Le timeout serveur est de **120 secondes** (`maxDuration`). Si l'agent met plus longtemps à répondre, la connexion sera coupée.

### Backpressure

Le serveur utilise le mécanisme `pull()` du `ReadableStream` : les chunks sont mis en file d'attente et envoyés dès que le client est prêt à les consommer. Si le client lit lentement, le serveur met les données en tampon.

### Interruption

Si le client ferme la connexion (signal `abort`), le serveur interrompt la génération et pousse un message d'abandon dans le flux avant de le fermer.

### Multipart/form-data

Le streaming fonctionne aussi avec un envoi `multipart/form-data` (pour joindre des fichiers). Les champs texte (`companyIdentifier`, `agentUUID`, `question`, `stream`) sont lus depuis le `FormData`, et les fichiers sont convertis en `data:` URL base64 avant d'être transmis à l'assistant.

***
