> 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/sandbox-vs-live.md).

# Sandbox vs Live + IP allowlist

Le mode (sandbox vs live) est **déterminé côté serveur** par la clé que tu présentes — pas par un paramètre de requête. Une clé `pk_sandbox_…` tape un wallet de test ; une clé `pk_live_…` tape l'argent réel.

## Tableau comparatif

| Critère                                 | Sandbox (`pk_sandbox_…`)                   | Live (`pk_live_…`)                       |
| --------------------------------------- | ------------------------------------------ | ---------------------------------------- |
| Wallet touché                           | Wallet sandbox (jouet, isolé)              | Wallet de production (argent réel)       |
| Providers externes (MTN, MOOV, Stripe…) | Endpoints sandbox                          | Endpoints production                     |
| Signature HMAC                          | Requise                                    | Requise                                  |
| Idempotency-Key                         | Requise sur POST/PUT/PATCH                 | Requise sur POST/PUT/PATCH               |
| **Whitelist IP**                        | **Désactivée** — appel depuis n'importe où | **Obligatoire** — sinon 403 systématique |
| Limites par opération                   | Permissives                                | Standards                                |
| Données stockées                        | Effacables sans impact                     | Auditées, comptables                     |

## La whitelist IP en mode live

C'est le filet de sécurité qui empêche qu'une clé live compromise soit utilisée depuis un serveur inconnu.

**Comment ça marche :**

1. Chaque marchand maintient une liste d'IPs ou de CIDR autorisés (max 20 entrées).
2. À chaque requête en mode live, l'API résout l'IP source (via `X-Real-IP` fourni par Nginx ou socket direct) et la compare à la liste.
3. Si l'IP correspond à une entrée exacte ou tombe dans un CIDR → la requête passe à l'étape suivante (vérification signature).
4. Sinon → 403, requête rejetée avant même de lire le body. La tentative est loggée (audit).

**Important — fail-closed** : une liste vide ne signifie pas "accepter tout". Elle signifie "rejeter tout". C'est volontaire : une clé live mal configurée ne peut jamais servir de trafic depuis l'Internet ouvert.

## Configurer la whitelist depuis le dashboard

* Menu **Développeurs → IPs autorisées**
* Récupère l'IP publique de ton serveur backend depuis ce serveur :

```bash
curl -4 ifconfig.me
```

{% hint style="warning" %}
**L'IP de ton navigateur n'est presque jamais la bonne** — c'est l'IP du serveur d'où ton backend va appeler l'API. Sauf si tu testes en local depuis la même machine que ton backend.
{% endhint %}

* Clique "Ajouter un serveur"
* Renseigne l'IP exacte (`203.0.113.4`) ou un bloc CIDR (`203.0.113.0/29` pour un cluster de 8 IPs derrière le même NAT)
* Sauvegarde — le changement est immédiat

## Formats acceptés

| Format      | Exemple          | Quand l'utiliser                                           |
| ----------- | ---------------- | ---------------------------------------------------------- |
| IPv4 exacte | `203.0.113.4`    | Un serveur unique                                          |
| IPv6 exacte | `2001:db8::1`    | Un serveur unique IPv6                                     |
| CIDR IPv4   | `203.0.113.0/29` | Cluster derrière un NAT, pool de sortie d'un load balancer |
| CIDR IPv6   | `2001:db8::/32`  | Bloc IPv6 attribué par ton fournisseur                     |

## Cas d'usage typiques

| Topologie                                 | Recommandation                                                        |
| ----------------------------------------- | --------------------------------------------------------------------- |
| 1 serveur backend, 1 IP fixe              | Ajoute l'IP exacte                                                    |
| Plusieurs serveurs derrière un NAT unique | Ajoute l'IP du NAT (une seule entrée)                                 |
| Cluster Kubernetes avec egress fixe       | Ajoute l'IP/CIDR de l'egress gateway                                  |
| Auto-scaling sans IP fixe                 | Mets en place un NAT egress avec IP statique, puis whitelist celle-ci |
| Plusieurs régions cloud                   | Ajoute chaque IP de sortie (max 20 entrées au total)                  |

{% hint style="danger" %}
Ne whitelistez pas `0.0.0.0/0` (ouvrir à tout) — l'API rejettera de toute façon une entrée aussi large, et c'est l'inverse du but recherché.
{% endhint %}

## Erreurs possibles

```json
// Liste vide sur clé live
HTTP 403
{
  "error": true,
  "code": "ip_allowlist_empty",
  "message": "Live API keys require at least one allowed IP. Configure it in your merchant dashboard before going live."
}

// IP appelante absente de la liste
HTTP 403
{
  "error": true,
  "code": "ip_not_allowed",
  "message": "This source IP is not in the merchant allowlist",
  "client_ip": "203.0.113.99"
}
```

## Tester en sandbox sans whitelist

1. Crée une clé sandbox depuis le dashboard (mode `SANDBOX`)
2. Utilise cette clé dans Postman / cURL / ton intégration locale — aucune restriction IP
3. Quand tout fonctionne, génère une clé live et configure la whitelist **avant** ton premier appel live


---

# 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/sandbox-vs-live.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.
