> For the complete documentation index, see [llms.txt](https://docs.intram.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.intram.org/merchant-api-v1/errors.md).

# Enveloppe de réponse & erreurs

Toutes les réponses du Merchant API suivent une enveloppe JSON standard.

## Succès synchrone

```json
HTTP 200
{
  "error": false,
  "data": { /* contenu spécifique à l'endpoint */ }
}
```

## Succès asynchrone (POST mutants)

```json
HTTP 202 Accepted
{
  "error": false,
  "http_status": 202,
  "data": {
    "operation_id": "op_2f4a…",
    "type": "payout",
    "status": "queued",
    "env": "live",
    "date": "2026-05-20T10:30:00.000Z",
    "_links": { "self": "/api/v1/merchant/operations/op_2f4a…" }
  }
}
```

L'opération est en file d'attente. Suis son avancement via webhook (recommandé) ou polling sur `GET /operations/:id`.

## Erreur

```json
HTTP 4xx | 5xx
{
  "error": true,
  "http_status": 400,
  "code":  "validation_error",
  "message": "Invalid payout request",
  "details": [ /* optionnel — détails par champ */ ]
}
```

| Champ         | Description                                                                             |
| ------------- | --------------------------------------------------------------------------------------- |
| `error`       | Toujours `true` en cas d'erreur, `false` en cas de succès                               |
| `http_status` | Écho du code HTTP de la réponse                                                         |
| `code`        | Identifiant machine-readable stable de l'erreur (à utiliser dans le code)               |
| `message`     | Message human-readable, peut évoluer entre versions                                     |
| `details`     | Optionnel — tableau de violations de validation par champ, ou objet provider-spécifique |

{% hint style="warning" %}
Toujours **dispatcher sur `code`**, jamais sur `message`. Le message peut être traduit ou reformulé sans préavis ; le code est garanti stable dans une version majeure.
{% endhint %}

## Catalogue des codes

### Authentification (401 / 403)

| Code                 | HTTP | Action                                                                               |
| -------------------- | :--: | ------------------------------------------------------------------------------------ |
| `missing_api_key`    |  401 | Ajoute le header `X-Api-Key`                                                         |
| `invalid_api_key`    |  401 | Vérifie la clé — peut-être révoquée ou mauvais env                                   |
| `signature_invalid`  |  401 | Recalcule la signature — voir [Authentification](/merchant-api-v1/authentication.md) |
| `merchant_inactive`  |  403 | Le compte n'est pas validé — contacte le support                                     |
| `ip_allowlist_empty` |  403 | Configure au moins une IP autorisée dans le dashboard                                |
| `ip_not_allowed`     |  403 | L'IP appelante n'est pas dans la whitelist live                                      |

### Idempotency (400 / 409)

| Code                      | HTTP | Action                                                                  |
| ------------------------- | :--: | ----------------------------------------------------------------------- |
| `missing_idempotency_key` |  400 | Ajoute le header `Idempotency-Key`                                      |
| `invalid_idempotency_key` |  400 | Reformule la clé (8-128 chars `[A-Za-z0-9_-]`)                          |
| `idempotency_conflict`    |  409 | Tu réutilises une clé avec un body différent — utilise une nouvelle clé |

### Validation (400)

| Code               | HTTP | Action                                              |
| ------------------ | :--: | --------------------------------------------------- |
| `validation_error` |  400 | Voir `details[]` pour la liste des champs invalides |
| `invalid_body`     |  400 | Le body n'est pas du JSON valide                    |
| `unknown_currency` |  400 | La devise demandée n'est pas supportée              |
| `amount_too_small` |  400 | Sous le minimum autorisé pour la devise             |
| `amount_too_large` |  400 | Au-dessus du maximum autorisé pour la devise        |

### Ressources (404 / 409)

| Code                    | HTTP | Action                                                                              |
| ----------------------- | :--: | ----------------------------------------------------------------------------------- |
| `transaction_not_found` |  404 | Le `transaction_reference` n'existe pas ou n'appartient pas à ce marchand           |
| `operation_not_found`   |  404 | L'`operation_id` n'existe pas ou n'appartient pas à ce marchand                     |
| `not_refundable`        |  400 | La transaction n'est pas dans un statut remboursable (déjà refunded, échouée, etc.) |

### Métier (réponses 200 avec opération `failed`)

Quand le worker traite une opération asynchrone et qu'elle échoue, la réponse `GET /operations/:id` contient un champ `error` avec un de ces codes :

| Code                         | Quand                                                           |
| ---------------------------- | --------------------------------------------------------------- |
| `insufficient_balance`       | Solde marchand insuffisant pour le payout                       |
| `merchant_wallet_missing`    | Aucun wallet provisionné pour ce marchand (cas rare)            |
| `provider_rejected`          | Le provider MoMo / banque a refusé l'opération                  |
| `provider_unavailable`       | Aucun handler pour le couple country/operator demandé           |
| `provider_error`             | Erreur réseau ou exception inattendue du provider               |
| `missing_provider_code`      | `destination.provider_code` absent pour un payout mobile\_money |
| `amount_exceeds_transaction` | Tentative de refund supérieure au montant original              |

### Infrastructure (5xx)

| Code                | HTTP | Action                                                           |
| ------------------- | :--: | ---------------------------------------------------------------- |
| `internal_error`    |  500 | Réessaie avec la **même** Idempotency-Key après un délai         |
| `auth_error`        |  500 | Erreur côté serveur sur la vérif de la clé — contacte le support |
| `idempotency_error` |  500 | Redis indisponible — réessaie après quelques secondes            |

## Différencier erreur synchrone vs erreur asynchrone

```javascript
const { data } = await signedPost('/payouts', payload, idem);

// Synchrone — la requête elle-même a-t-elle été acceptée ?
if (data.error) {
  console.error('Rejet immédiat:', data.code, data.message);
  return;
}

// Acceptée — surveiller l'opération via webhook ou polling
const opId = data.data.operation_id;

// Polling alternatif (déconseillé — préfère les webhooks)
const op = await signedGet(`/operations/${opId}`);
if (op.data.status === 'failed') {
  console.error('Échec métier:', op.data.error.code, op.data.error.message);
}
```

## Statuts d'opération

| `status`     | Sens                                                   |
| ------------ | ------------------------------------------------------ |
| `queued`     | En file d'attente, pas encore traitée                  |
| `processing` | En cours de traitement par un worker                   |
| `succeeded`  | Terminée avec succès — voir `result`                   |
| `failed`     | Échouée — voir `error.code` et `error.message`         |
| `expired`    | Pas encore utilisé (réservé pour les opérations à TTL) |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.intram.org/merchant-api-v1/errors.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
