# Authentification

import { ApiExample, ApiResponse, StagingUrl } from "../../src/ApiExample";

L'API partenaire s'authentifie par **clé d'API**, présentée en en-tête sur chaque requête. Il n'y a ni login, ni mot de passe, ni jeton à rafraîchir : la clé est le seul secret.

```http title="En-tête"
Authorization: Bearer ifp_live_9f2c4a7b1e8d3406af5b2c9d1e0f7a83
```

<Callout type="note" title="Pas de connexion utilisateur ici">
  `/v1/papi/*` est une API machine, faite pour votre serveur. Les comptes humains de vos clients (gestionnaire de flotte, directeur financier) se connectent sur le tableau de bord Instafuel avec leur propre mot de passe — ce parcours ne passe pas par cette API et n'est pas documenté ici.
</Callout>

## Un appel authentifié

<ApiExample path="/v1/papi/me" />

<ApiResponse
  status={200}
  body={{
    ref: "APP-7C3D91B0",
    name: "Ivoire Fleet Partners",
    email: "contact@ivoirefleet.ci",
    environment: "test",
    companiesCount: 12,
    walletCreditEnabled: true,
    scopes: ["COMPANIES_READ", "TRANSACTIONS_READ", "WALLET_READ", "COMPANIES_WRITE", "CREDIT_REQUEST_WRITE", "REPORTS_READ"],
    caps: {
      perOperationFcfa: 5000000,
      dailyFcfa: 20000000,
      dailyRemainingFcfa: 14500000,
    },
  }}
/>

`GET /v1/papi/me` est le bon appel de contrôle : il ne modifie rien, coûte peu, et vous dit exactement ce que la clé permet.

## Les deux préfixes

| Préfixe | Environnement | URL de base | Effet |
|---|---|---|---|
| `ifp_test_*` | staging | <StagingUrl /> | données de test, aucun argent réel |
| `ifp_live_*` | production | communiquée avec la clé | argent réel |

Le préfixe fait partie de la clé : il ne se devine pas, il se lit. Une clé de test présentée en production est rejetée en `401`, et réciproquement — les deux environnements ne partagent aucune donnée.

Cette séparation vous donne un contrôle simple à automatiser : si votre code de test manipule une clé qui ne commence pas par `ifp_test_`, arrêtez-le avant l'appel.

```javascript title="JavaScript"
if (
  process.env.NODE_ENV !== "production" &&
  !process.env.INSTAFUEL_API_KEY.startsWith("ifp_test_")
) {
  throw new Error("Clé de production détectée hors production — arrêt.");
}
```

```python title="Python"
import os

cle = os.environ["INSTAFUEL_API_KEY"]

if os.environ.get("APP_ENV") != "production" and not cle.startswith("ifp_test_"):
    raise SystemExit("Clé de production détectée hors production — arrêt.")
```

## Où mettre la clé

<Callout type="danger" title="Une clé d'API n'est jamais côté client">
  Elle donne accès à l'intégralité de votre portefeuille. Elle vit dans une variable d'environnement ou un gestionnaire de secrets, sur votre serveur. Jamais dans un dépôt Git, jamais dans un bundle JavaScript de navigateur, jamais dans une application mobile — même « obfusquée », elle est extractible en quelques minutes.
</Callout>

Si votre interface web doit afficher les données de votre portefeuille, faites-la passer par votre propre backend : c'est lui qui porte la clé et applique vos propres règles d'accès.

## Erreurs d'authentification

| HTTP | `error.code` | Cause | Que faire |
|---|---|---|---|
| 401 | `PAPI_KEY_MISSING` | en-tête `Authorization` absent | ajoutez l'en-tête |
| 401 | `PAPI_KEY_INVALID` | clé inconnue, révoquée, ou du mauvais environnement | vérifiez le préfixe et l'URL de base |
| 401 | `PAPI_KEY_EXPIRED` | clé arrivée à échéance | générez-en une nouvelle, voir [Clés d'API](/guides/cles-api) |
| 403 | `PAPI_IP_FORBIDDEN` | l'adresse appelante n'est pas dans la liste autorisée | ajoutez l'IP sortante de votre serveur |
| 403 | `PAPI_SCOPE_MISSING` | la clé n'a pas le droit nécessaire à cette opération | utilisez une clé portant le bon droit |
| 403 | `RESELLER_INACTIVE` | compte apporteur désactivé | contactez Instafuel |

Une clé révoquée après une rotation mal terminée donne ceci — c'est la trace à chercher dans vos journaux quand une intégration tombe d'un coup :

<ApiResponse
  status={401}
  body={{
    error: {
      code: "PAPI_KEY_INVALID",
      message: "Clé d'API inconnue ou révoquée",
    },
  }}
/>

Une clé valide mais dépourvue du droit demandé :

<ApiResponse
  status={403}
  body={{
    error: {
      code: "PAPI_SCOPE_MISSING",
      message: "Cette clé ne porte pas le droit CREDIT_REQUEST_WRITE",
      requiredScope: "CREDIT_REQUEST_WRITE",
    },
  }}
/>

Branchez votre logique sur `error.code`, jamais sur `message` — voir [Erreurs et idempotence](/guides/erreurs).

## Ce que la clé détermine

La clé porte votre identité **et** votre périmètre. Vous ne choisissez pas l'entreprise pour laquelle vous appelez : vous appelez, et le serveur restreint la réponse à votre portefeuille. Aucun paramètre n'élargit cette vue — voir [Périmètre](/guides/perimetre).

Elle porte aussi ses droits : une clé en lecture seule renvoie `403 PAPI_SCOPE_MISSING` sur toute écriture, sans effet de bord.
