---
seo_title: Authentification API : X-API-Key et JWT | EnConvert
meta_desc: EnConvert a deux types de clés : les clés privées sk_ pour vos serveurs et les clés publiques pk_ échangées contre un JWT côté navigateur, plus GET /v1/auth/verify.
keywords: authentification api en-tête x-api-key, jeton jwt bearer api, clé publique authentification côté client, différence entre clé sk_ et pk_, échanger une clé publique contre un jwt, endpoint /v1/auth/token, vérifier une clé api, rotation de clé api sans interruption, liste blanche de domaines api, erreur 403 allowed_endpoints
---

# Authentification

EnConvert propose deux types de clé API, et c'est l'endroit où votre code s'exécute qui décide de celui que vous utilisez. Une clé privée (`sk_`) se place dans l'en-tête `X-API-Key` depuis un serveur que vous contrôlez ; une clé publique (`pk_`) s'échange contre un JWT de courte durée que le code du navigateur envoie dans `Authorization: Bearer <token>`.

---

## Choisir un type de clé

| | Clé privée | Clé publique + JWT |
|---|---|---|
| Préfixe | `sk_` | `pk_` |
| Envoyée comme | `X-API-Key: sk_your_private_key` | `Authorization: Bearer <token>` |
| S'exécute dans | serveurs, scripts, jobs CI, conteneurs | navigateurs, widgets embarqués, tout ce qui est livré à un client |
| Rejetée quand | la requête porte un en-tête `Origin` (`403`) | elle appelle autre chose que `/v1/auth/token` ou `/v1/auth/branding` (`403`) |
| Portée | tous les endpoints : sync, async, batch, webhooks | un élément par requête, synchrone, téléchargement présigné |
| Restrictions | `allowed_endpoints` en option | `allowed_domains` plus `allowed_endpoints` |
| Durée de vie | clé valide jusqu'à révocation | clé valide jusqu'à révocation, jeton d'accès 1 heure, cookie de rafraîchissement 7 jours |

Choisissez selon la cible de déploiement. Un service backend, un script, une tâche cron ou un outil interne prend une clé privée. Une application navigateur ou un widget embarqué prend une clé publique avec JWT. Il n'existe aucun moyen de cacher une clé privée dans du code frontend : la passerelle la rejette sur la seule présence d'un en-tête `Origin`, avant même de vérifier quoi que ce soit d'autre.

Une clé, c'est son préfixe suivi d'un jeton aléatoire, soit 46 caractères au total. Les noms d'espace réservé des exemples ci-dessous (`sk_your_private_key`, `pk_your_public_key`) remplacent ce jeton ; une vraie clé ne contient aucun segment d'environnement du type `live` ou `test`. Toute valeur de moins de 45 caractères est rejetée avec `401 Invalid API Key format` avant même que le préfixe soit lu.

Seul un hachage SHA-256 de chaque clé est stocké sur le serveur, accompagné d'un fragment de préfixe de sept caractères qui vous permet de distinguer vos clés dans le tableau de bord.

---

## Clés privées {: #private-keys }

Les clés privées sont destinées aux **applications côté serveur** où votre clé API peut rester secrète. Elles offrent un accès complet à tous les endpoints et fonctionnalités de l'API.

- **En-tête :** `X-API-Key: sk_your_private_key`
- **Accès :** Accès complet à tous les endpoints, y compris les opérations synchrones et asynchrones, le traitement par lots et tous les types de conversion.
- **Sécurité :** Les clés sont stockées sous forme de hachages SHA-256 sur le serveur. La clé en clair n'est affichée qu'une seule fois, au moment de la création.

Aucun échange de jeton ni gestion de session n'est requis. Incluez la clé dans chaque requête :

```bash
curl -X POST https://api.enconvert.com/v1/convert/url-to-pdf \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'
```

### Format de l'en-tête

Incluez votre clé privée dans l'en-tête `X-API-Key` à chaque requête :

```http
X-API-Key: sk_your_private_key
```

Les clés privées commencent toujours par le préfixe `sk_`. Vous pouvez générer et gérer vos clés depuis le tableau de bord EnConvert.

### Exemple : conversion de fichier

Convertissez un fichier JSON en XML à l'aide d'une clé privée :

```bash
curl -X POST https://api.enconvert.com/v1/convert/json-to-xml \
  -H "X-API-Key: sk_your_private_key" \
  -F "file=@data.json"
```

Réponse :

```json
{
  "presigned_url": "https://econverter.nyc3.cdn.digitaloceanspaces.com/...",
  "object_key": "live/files/12345/json-to-xml/data_20250202_120530123.xml",
  "filename": "data_20250202_120530123.xml",
  "file_size": 1024,
  "conversion_time_seconds": 0.45
}
```

- `presigned_url` : une URL temporaire et téléchargeable permettant de récupérer le fichier converti.
- `object_key` : le chemin de stockage du fichier converti (par exemple, `live/files/12345/json-to-xml/...`). Ce n'est pas une URL.
- `filename` : le nom de fichier généré pour le fichier converti.
- `file_size` : la taille du fichier de sortie en octets.
- `conversion_time_seconds` : le temps nécessaire pour effectuer la conversion.

Ces URL de téléchargement sont de courte durée. Leurs règles d'expiration et de rétention figurent sur [URL signées](/fr/docs/concepts/signed-urls.md).

### Restrictions d'endpoints

Par défaut, une clé privée a accès à tous les endpoints de l'API. Vous pouvez éventuellement restreindre une clé à des endpoints spécifiques à l'aide du paramètre **allowed_endpoints** au moment de sa création.

Lorsque `allowed_endpoints` est configuré, la clé ne pourra appeler que les endpoints répertoriés. Les requêtes vers tout autre endpoint seront rejetées avec une erreur `403 Forbidden` : `Endpoint '{path}' not allowed for this API key`.

Exemple de configuration :

```json
{
  "allowed_endpoints": [
    "/v1/convert/url-to-pdf",
    "/v1/convert/json-to-xml",
    "/v1/convert/html-to-pdf"
  ]
}
```

C'est utile lorsque vous souhaitez émettre une clé à portée limitée, par exemple une clé qui ne peut effectuer que des conversions PDF.

Quelques chemins restent accessibles quoi que dise la liste, parce qu'une clé capable de lancer une tâche doit pouvoir la mener à son terme :

- `/v1/auth/token`, `/v1/auth/verify` et `/v1/whoami`
- `/v1/convert/status/{job_id}`, `/v1/convert/batch/{batch_id}` et `/v1/convert/download/{object_key}`
- `/v1/extension/*`, pour les requêtes authentifiées par JWT
- les chemins V2 par tâche de perceive, ingest et watch

Une clé créée avec l'unique entrée `["*"]` signifie tous les endpoints, y compris ceux qui seront livrés après la création de la clé.

La liste est figée au moment de la création. Aucun appel ne permet de modifier les restrictions d'une clé existante : restreindre ou élargir la portée d'une clé revient donc à en créer une nouvelle et à révoquer l'ancienne. Voir [Protéger vos clés](#keeping-keys-safe).

<div class="alert alert-warning">
<strong>N'utilisez pas de clés privées dans du code côté client.</strong> L'API détecte l'en-tête <code>Origin</code> envoyé par les navigateurs et rejettera les requêtes effectuées avec une clé privée depuis un environnement navigateur avec <code>403 Private API keys cannot be used from browsers</code>. Pour les intégrations côté client, utilisez plutôt une <a href="#public-keys-and-jwt">clé publique avec JWT</a>.
</div>

---

## Clés publiques et JWT {: #public-keys-and-jwt }

L'authentification par clé publique et JWT permet aux applications côté client (navigateur) d'appeler l'API EnConvert : vous échangez votre clé publique (`pk_`) contre un jeton d'accès JWT de courte durée via `POST /v1/auth/token`, puis vous envoyez ce jeton dans l'en-tête `Authorization: Bearer <token>` sur les requêtes API. Comme une clé publique est visible par les utilisateurs finaux, elle ne peut pas appeler l'API directement. Seule, elle atteint exactement deux chemins, `/v1/auth/token` et `/v1/auth/branding`. Tout le reste renvoie `403` avec un message vous invitant à échanger d'abord la clé contre un jeton.

1. **Échangez** votre clé publique (`pk_`) contre un jeton d'accès JWT en appelant `POST /v1/auth/token`.
2. **Utilisez** le jeton JWT dans l'en-tête `Authorization: Bearer <token>` sur les requêtes API.
3. **Rafraîchissez** le jeton automatiquement avant son expiration à l'aide de `POST /v1/auth/refresh`.
4. **La liste blanche de domaines** garantit que seules les requêtes provenant de vos domaines autorisés sont acceptées.

### Étape 1 : échanger la clé publique contre un JWT

```http
POST /v1/auth/token
```

| En-tête | Valeur | Description |
|---|---|---|
| `X-API-Key` | `pk_your_public_key` | Votre clé API publique |

L'endpoint attend un objet JSON dans le corps de la requête. N'envoyer aucun corps renvoie `422` avec `{"type":"missing","loc":["body"],"msg":"Field required"}` : envoyez donc `{}` quand vous n'avez rien à transmettre. Le seul champ optionnel est `turnstile_token`, vérifié uniquement pour les requêtes provenant de l'origine du widget EnConvert et ignoré partout ailleurs.

```javascript
async function getToken() {
  const response = await fetch("https://api.enconvert.com/v1/auth/token", {
    method: "POST",
    headers: {
      "X-API-Key": "pk_your_public_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({}),
    credentials: "include",
  });

  if (!response.ok) {
    throw new Error(`Token exchange failed: ${response.status}`);
  }

  const data = await response.json();
  return data.token;
}
```

<div class="alert alert-warning">
<strong>Important :</strong> Vous devez inclure <code>credentials: "include"</code> dans les options du fetch. Cela garantit que le cookie du jeton de rafraîchissement est stocké par le navigateur, ce qui est nécessaire pour le rafraîchissement automatique du jeton.
</div>

Réponse :

```json
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

La réponse définit également un cookie `HttpOnly` contenant le jeton de rafraîchissement. Ce cookie est géré automatiquement par le navigateur et utilisé lors du rafraîchissement du jeton d'accès.

Une clé privée envoyée à cet endpoint est refusée avec `400 Only public API keys can exchange for tokens. Private keys should be used directly.` C'est l'API qui vous dit de supprimer l'étape d'échange, pas une clé cassée.

### Étape 2 : utiliser le jeton JWT

Incluez le jeton JWT dans l'en-tête `Authorization` en tant que jeton Bearer sur toutes les requêtes API suivantes.

```javascript
async function convertUrlToPdf(token, url) {
  const response = await fetch("https://api.enconvert.com/v1/convert/url-to-pdf", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ url }),
  });

  return await response.json();
}

// Usage
const token = await getToken();
const result = await convertUrlToPdf(token, "https://example.com");
console.log(result.presigned_url);
```

### Étape 3 : rafraîchissement automatique du jeton

Les jetons d'accès expirent au bout d'une heure. Utilisez l'endpoint de rafraîchissement pour obtenir un nouveau jeton d'accès sans obliger l'utilisateur à se ré-authentifier.

```http
POST /v1/auth/refresh
```

Le jeton de rafraîchissement est envoyé automatiquement via le cookie `HttpOnly` défini lors de l'échange de jeton initial. Aucun corps de requête ni en-tête supplémentaire n'est nécessaire.

```javascript
class EnconvertClient {
  constructor(publicKey) {
    this.publicKey = publicKey;
    this.token = null;
    this.tokenExpiry = null;
  }

  async getToken() {
    const response = await fetch("https://api.enconvert.com/v1/auth/token", {
      method: "POST",
      headers: {
        "X-API-Key": this.publicKey,
        "X-Parent-Origin": window.location.origin,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({}),
      credentials: "include",
    });

    const data = await response.json();
    this.token = data.token;
    // Set expiry to 55 minutes (refresh before the 1-hour expiry)
    this.tokenExpiry = Date.now() + 55 * 60 * 1000;
    return this.token;
  }

  async refreshToken() {
    const response = await fetch("https://api.enconvert.com/v1/auth/refresh", {
      method: "POST",
      credentials: "include",
    });

    if (!response.ok) {
      // Refresh token expired, re-authenticate
      return await this.getToken();
    }

    const data = await response.json();
    this.token = data.token;
    this.tokenExpiry = Date.now() + 55 * 60 * 1000;
    return this.token;
  }

  async getValidToken() {
    if (!this.token || Date.now() >= this.tokenExpiry) {
      if (this.token) {
        return await this.refreshToken();
      }
      return await this.getToken();
    }
    return this.token;
  }

  async convert(endpoint, body) {
    const token = await this.getValidToken();
    const response = await fetch(`https://api.enconvert.com${endpoint}`, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });

    return await response.json();
  }
}

// Usage
const client = new EnconvertClient("pk_your_public_key");
const result = await client.convert("/v1/convert/url-to-pdf", {
  url: "https://example.com",
});
```

Quatre choses à savoir avant de déboguer un rafraîchissement qui échoue :

- Le rafraîchissement retrouve votre projet à partir du cookie, puis cherche n'importe quelle clé publique active sur ce projet. Révoquez toutes les clés publiques et le rafraîchissement se met à renvoyer `401`, même si le cookie est encore dans ses sept jours.
- La route de rafraîchissement ne revérifie pas la liste blanche de domaines. Cette vérification a lieu au moment où le jeton est émis.
- Le rafraîchissement ne relie pas le nouveau jeton à la clé que vous aviez utilisée au départ. Si le projet détient plusieurs clés publiques, le jeton rafraîchi peut revenir avec les restrictions d'une autre clé.
- L'émission et le rafraîchissement de jetons ont leur propre limitation par IP, distincte des [limites de débit](/fr/docs/reference/rate-limits.md) de votre offre. Un client coincé dans une boucle de rafraîchissement s'en apercevra.

### Durées de vie des jetons

| Jeton | Durée de vie | Stockage |
|---|---|---|
| Jeton d'accès | 1 heure | Renvoyé dans le corps de la réponse JSON ; à stocker en mémoire |
| Jeton de rafraîchissement | 7 jours | Défini en tant que cookie `HttpOnly` ; géré par le navigateur |

### Liste blanche de domaines

Les clés publiques sont restreintes à des domaines spécifiques configurés dans votre tableau de bord.

La correspondance ne compare que l'hôte et le port. Le schéma est d'abord retiré des deux côtés, si bien que `https://example.com` et `http://example.com` désignent la même origine du point de vue de la liste blanche. Le port, lui, n'est pas retiré et fait partie de la comparaison.

- **Correspondance exacte :** `https://example.com` correspond à l'hôte nu `example.com` quel que soit le schéma.
- **Sous-domaines génériques :** `https://*.example.com` correspond à `https://app.example.com`, `https://staging.example.com`, et aussi au domaine apex `https://example.com`.
- **Spécifique à un port :** `http://localhost:3000` ne correspond qu'à cet hôte et ce port.

| Entrée de la liste blanche | Correspond à | Ne correspond pas à |
|---|---|---|
| `https://example.com` | `https://example.com`, `http://example.com` | `https://www.example.com` |
| `https://*.example.com` | `https://app.example.com`, `https://dev.example.com`, `https://example.com` | `https://example.net` |
| `http://localhost:3000` | `http://localhost:3000` | `http://localhost:8080` |

Une requête provenant d'une origine absente de la liste reçoit `403 Domain {origin} not authorized`, et le propriétaire du projet en est averti par e-mail (au plus une fois par clé et par 24 heures). Si votre boîte de réception se remplit, la cause habituelle est une entrée obsolète dans la liste.

Deux origines échappent complètement à la vérification de domaine : une origine `chrome-extension://...`, pour que les extensions de navigateur puissent appeler l'API, et l'origine du widget EnConvert, où la vérification est remplacée par la validation de l'en-tête `X-Parent-Origin` du widget.

### Fonctionnalités de sécurité

- **Jetons de courte durée :** Les jetons d'accès expirent au bout d'1 heure, ce qui limite la fenêtre d'exposition en cas de compromission d'un jeton.
- **Cookies de rafraîchissement HttpOnly :** Les jetons de rafraîchissement sont stockés dans des cookies `HttpOnly`, ce qui les rend inaccessibles au JavaScript et résistants aux attaques XSS.
- **Restrictions de domaine :** Les jetons ne sont délivrés que lorsque la requête provient d'un domaine autorisé.
- **Pas d'accès direct à l'API :** Les clés publiques seules ne peuvent pas appeler les endpoints de conversion. Un JWT valide est toujours requis.

### Restrictions de la clé publique

L'authentification par clé publique présente les limitations suivantes par rapport aux clés privées :

- **Synchrone uniquement :** Seuls les endpoints de conversion synchrones sont disponibles. Le mode asynchrone et les rappels webhook ne le sont pas ; `notification_email` et `callback_url` sont effacés sur les conversions effectuées avec une clé navigateur.
- **Un seul élément par requête :** Chaque requête ne peut convertir qu'une seule URL ou qu'un seul fichier. L'envoi d'un tableau renvoie `400 Public keys only support a single URL input`.
- **Téléchargement direct :** Les réponses fournissent une `presigned_url` pour un téléchargement immédiat. Il n'existe aucune option pour des destinations de stockage personnalisées.
- **Statut de tâche oui, statut de lot non :** `GET /v1/convert/status/{job_id}` fonctionne avec un jeton navigateur, ce qui permet au widget de récupérer un résultat après une connexion interrompue. `GET /v1/convert/batch/{batch_id}` est refusé avec `403 Batch status requires a private API key`.

La soumission de lots elle-même dépend de la limite de lots de votre offre plutôt que du type de clé, mais comme une clé navigateur est plafonnée à un élément par requête, les lots exigent en pratique une clé privée. Voir [Traitement par lots](/fr/docs/guides/batch-processing.md).

<div class="alert alert-warning">
<strong>Bonnes pratiques :</strong>
<ul>
  <li>Stockez toujours les jetons d'accès uniquement en mémoire. Ne les persistez jamais dans <code>localStorage</code> ou <code>sessionStorage</code>.</li>
  <li>Mettez en place le rafraîchissement automatique des jetons pour éviter les interruptions pendant les sessions utilisateur.</li>
  <li>Gardez votre liste de domaines autorisés aussi précise que possible. Évitez les caractères génériques trop larges.</li>
  <li>Utilisez <code>credentials: "include"</code> sur toutes les requêtes fetch pour garantir l'envoi et la réception corrects des cookies.</li>
  <li>Gérez les échecs de rafraîchissement de jeton avec élégance en revenant à une ré-authentification complète avec la clé publique.</li>
</ul>
</div>

Si vous voulez le flux navigateur sans en écrire une ligne, le widget intégrable émet et rafraîchit ses propres jetons. Voir [Widgets web](/fr/docs/guides/integrations.md#web-widgets).

---

## Vérifier vos identifiants {: #verify-your-credentials }

`GET /v1/auth/verify` vérifie si votre authentification actuelle est valide et rapporte ce que l'API croit qu'elle est. Il fonctionne avec les clés privées envoyées dans l'en-tête `X-API-Key` et avec les jetons JWT Bearer envoyés dans l'en-tête `Authorization`. Une requête valide renvoie votre `project_id`, `tier`, `key_type`, ainsi que toute restriction de domaine ou d'endpoint ; une clé ou un jeton invalide ou expiré renvoie `401 Unauthorized`.

```http
GET /v1/auth/verify
```

| En-tête | Valeur | Description |
|---|---|---|
| `X-API-Key` | `sk_your_private_key` | S'authentifier avec une clé privée |
| `Authorization` | `Bearer <token>` | S'authentifier avec un jeton JWT |

Utilisez l'un des deux en-têtes ci-dessus, pas les deux.

<div class="alert alert-warning">
<strong>Vous ne pouvez pas vérifier une clé publique directement.</strong> Envoyer <code>X-API-Key: pk_...</code> à cet endpoint renvoie <code>403</code>, car une clé publique ne peut appeler que <code>/v1/auth/token</code> et <code>/v1/auth/branding</code>. Émettez d'abord un jeton, puis vérifiez le jeton. C'est le seul cas où un <code>403</code> ici ne signifie pas que votre clé est cassée.
</div>

Avec une clé privée :

```bash
curl https://api.enconvert.com/v1/auth/verify \
  -H "X-API-Key: sk_your_private_key"
```

Avec un jeton JWT :

```bash
curl https://api.enconvert.com/v1/auth/verify \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
```

Réponse :

```json
{
  "authenticated": true,
  "project_id": "12345",
  "tier": "pro",
  "key_type": "public",
  "allowed_domains": ["https://example.com", "https://*.example.com"],
  "allowed_endpoints": ["/v1/convert/url-to-pdf", "/v1/convert/jpeg-to-png"]
}
```

| Champ | Type | Description |
|---|---|---|
| `authenticated` | boolean | Toujours `true` pour une requête valide |
| `project_id` | string | L'identifiant de votre projet |
| `tier` | string | Votre niveau d'abonnement (par exemple, `free`, `starter`, `pro`, `business`) |
| `key_type` | string | `private`, `public` ou `dashboard` |
| `allowed_domains` | array or null | Domaines autorisés (clés publiques uniquement, `null` sinon) |
| `allowed_endpoints` | array or null | Endpoints restreints (clés publiques uniquement, `null` sinon) |

Deux détails font trébucher. `key_type` a une troisième valeur, `dashboard`, que le backend émet pour une session connectée du tableau de bord ou du playground ; comme une clé privée, elle renvoie `null` pour les deux listes. Et `tier` est le slug de l'offre, pas le nom affiché sur la page des tarifs : un abonnement Studio renvoie `"tier": "pro"`. Les slugs `free`, `starter`, `pro`, `business` et `enterprise` correspondent à Founding, Indie, Studio, Production et Enterprise.

Si la clé ou le jeton est invalide ou expiré, l'API renvoie une erreur `401 Unauthorized` à la place. La liste complète des messages d'erreur d'authentification figure dans [Erreurs](/fr/docs/reference/errors.md).

### GET /v1/whoami

Il existe un second endpoint d'identité, plus réduit. Il exige une clé privée :

```bash
curl https://api.enconvert.com/v1/whoami \
  -H "X-API-Key: sk_your_private_key"
```

```json
{
  "project_id": "12345",
  "plan_slug": "pro"
}
```

Il ne renvoie rien d'autre, et c'est délibéré : pas de type de clé, pas de domaines, pas de limites. Un JWT ou une clé publique reçoit `403 GET /v1/whoami requires a private API key (sk_...).` Plusieurs intégrations s'en servent comme test d'identifiants, dont le nœud n8n.

### Cas d'usage

- **Tester des clés API :** Confirmez qu'une clé nouvellement créée est active et correctement configurée.
- **Vérifier les restrictions de domaine :** Vérifiez quels domaines sont autorisés pour une clé publique.
- **Déboguer des problèmes d'authentification :** Déterminez si l'échec d'une requête est dû à l'authentification ou à autre chose.

---

## Protéger vos clés {: #keeping-keys-safe }

- **Stockage haché :** Les clés privées sont stockées sur le serveur sous forme de hachages SHA-256. La clé en clair n'est affichée qu'une seule fois, au moment de sa création. Si vous la perdez, vous devez générer une nouvelle clé.
- **Variables d'environnement :** Stockez votre clé dans une variable d'environnement (par exemple, `ENCONVERT_API_KEY`) plutôt que de la coder en dur dans votre code source.
- **Portée définie à la création :** `allowed_endpoints` et `allowed_domains` sont définis à la création de la clé et ne peuvent plus être modifiés ensuite. Décidez de la portée avant de cliquer sur créer.

### Faire tourner une clé

La rotation consiste à créer puis révoquer, et dans cet ordre elle ne coûte aucune interruption :

1. Créez la nouvelle clé dans le tableau de bord avec la portée voulue.
2. Déployez-la, puis confirmez que la nouvelle clé est active avec `GET /v1/auth/verify`.
3. Révoquez l'ancienne clé.

Les deux clés fonctionnent pendant l'étape 2, il n'existe donc aucune fenêtre où votre service n'est pas authentifié. Comme les restrictions sont immuables, changer la portée d'une clé suit exactement la même procédure que sa rotation.

### Si une clé fuite

Révoquez-la d'abord, mesurez l'ampleur des dégâts ensuite. La révocation est le seul coupe-circuit, car la portée d'une clé active ne peut pas être réduite ; une clé révoquée est rejetée avec `401 API Key revoked`.

- Une **clé privée** ayant fuité peut appeler tous les endpoints couverts par sa portée et consomme vos opérations mensuelles. Révoquez-la, créez une clé de remplacement, et vérifiez dans le tableau de bord si votre consommation contient des appels que vous n'avez pas faits.
- Une **clé publique** ayant fuité est moins urgente, par conception. Elle ne peut pas du tout appeler les endpoints de conversion, et elle n'émet des jetons que pour les origines de sa liste blanche. Resserrer cette liste implique de créer une clé plus étroite et de révoquer celle qui a fuité, puisque la liste d'une clé existante ne peut pas être modifiée.
- Un **jeton d'accès** ayant fuité meurt dans l'heure et ne peut pas être utilisé depuis une autre origine que celle pour laquelle il a été émis. Son cookie de rafraîchissement est le problème de plus longue durée : le rafraîchissement réussit tant que le projet possède au moins une clé publique active, donc révoquer la clé dont vient le jeton ne tue pas le cookie, sauf si c'était votre dernière clé publique.

<div class="alert alert-warning">
<strong>Une clé poussée sur git est déjà publique.</strong> Révoquez-la dans le tableau de bord avant de réécrire l'historique. Supprimer le commit ne dépublie pas la clé.
</div>

---

## Questions fréquentes

### Comment m'authentifier à une API REST avec un en-tête X-API-Key ?

Envoyez votre clé privée dans l'en-tête `X-API-Key` à chaque requête, par exemple `X-API-Key: sk_your_private_key`. Les clés privées donnent un accès complet à tous les endpoints (y compris les opérations synchrones et asynchrones, le traitement par lots et tous les types de conversion) sans nécessiter d'échange de jeton.

### Quelle est la différence entre les clés API sk_ et pk_ ?

Les clés avec le préfixe `sk_` sont des clés privées destinées à un usage serveur à serveur et donnent un accès complet à l'API via l'en-tête `X-API-Key`. Les clés avec le préfixe `pk_` sont des clés publiques destinées aux applications côté client (navigateur) : elles ne peuvent pas appeler l'API directement et doivent d'abord être échangées contre un JWT de courte durée via `POST /v1/auth/token`.

### Puis-je utiliser ma clé API privée (sk_) dans un navigateur ou une application mobile ?

Non. L'API détecte l'en-tête `Origin` envoyé par les navigateurs et rejette les requêtes effectuées avec des clés privées depuis des environnements navigateur avec `403 Private API keys cannot be used from browsers`. Utilisez plutôt une clé publique (`pk_`) avec le flux JWT pour les intégrations côté client.

### Comment obtenir un jeton JWT Bearer pour l'authentification API côté client ?

Échangez votre clé publique (`pk_`) contre un JWT en appelant `POST /v1/auth/token` avec la clé dans l'en-tête `X-API-Key` et `{}` comme corps JSON. Utilisez le jeton renvoyé dans l'en-tête `Authorization: Bearer <token>` des requêtes API, et actualisez-le avant son expiration via `POST /v1/auth/refresh`.

### Puis-je restreindre une clé API privée à des endpoints spécifiques ?

Oui. Configurez `allowed_endpoints` au moment où vous créez la clé, en listant des chemins comme `/v1/convert/url-to-pdf`. Les requêtes vers tout endpoint non listé sont rejetées avec une erreur `403 Forbidden`, à l'exception des chemins d'authentification, de statut, de téléchargement et par tâche, qui restent accessibles à toutes les clés.

### Que se passe-t-il si je perds ma clé API privée ?

Les clés privées sont stockées sur le serveur sous forme de hachages SHA-256, et la clé en clair n'est affichée qu'une seule fois, au moment de sa création. Si vous la perdez, vous devez en générer une nouvelle. Vous pouvez créer plusieurs clés et révoquer les anciennes depuis le tableau de bord sans interruption de service.

### Combien de temps durent les jetons d'accès et les jetons de rafraîchissement ?

Les jetons d'accès expirent au bout d'1 heure et doivent être stockés uniquement en mémoire. Les jetons de rafraîchissement durent 7 jours et sont définis en tant que cookie `HttpOnly` géré par le navigateur.

### Pourquoi le rafraîchissement de mon jeton échoue-t-il sans credentials: "include" ?

Le jeton de rafraîchissement est stocké dans un cookie `HttpOnly` défini lors de l'échange de jeton initial, et `POST /v1/auth/refresh` repose sur l'envoi automatique de ce cookie par le navigateur. Si vous omettez `credentials: "include"` de vos requêtes fetch, le cookie n'est ni enregistré ni envoyé. En cas d'échec du rafraîchissement, revenez à une ré-authentification complète avec votre clé publique.

### Puis-je utiliser des sous-domaines génériques dans la liste blanche de domaines ?

Oui. `https://*.example.com` correspond à `https://app.example.com`, `https://staging.example.com`, et au domaine apex `https://example.com`. Les hôtes exacts et les origines spécifiques à un port comme `http://localhost:3000` sont également pris en charge. La correspondance ignore le schéma, mais pas le port.

### Puis-je utiliser une clé publique pour des conversions asynchrones ou par lots ?

Non. L'authentification par clé publique ne prend en charge que les endpoints de conversion synchrones, avec une seule URL ou un seul fichier par requête ; le mode asynchrone, les webhooks et l'interrogation du statut des lots nécessitent une clé privée. Les réponses fournissent une `presigned_url` pour un téléchargement immédiat.

### Comment tester si ma clé API est valide ?

Envoyez une requête à `GET /v1/auth/verify` avec une clé privée dans l'en-tête `X-API-Key`, ou un JWT dans l'en-tête `Authorization`. Un identifiant valide renvoie `authenticated: true` ainsi que votre `project_id` et votre `tier` ; un identifiant invalide ou expiré renvoie `401 Unauthorized`. Une clé publique ne peut pas être vérifiée de cette façon et renvoie `403`.

### Pourquoi allowed_domains et allowed_endpoints sont-ils null dans la réponse de vérification ?

Les deux champs ne sont renseignés que pour les clés publiques et renvoient `null` pour les clés privées et pour les sessions de tableau de bord. Pour les clés publiques, `allowed_domains` liste les domaines autorisés et `allowed_endpoints` liste les éventuelles restrictions d'endpoints.

### Quelle méthode d'authentification dois-je choisir pour mon intégration ?

Utilisez une clé privée (`sk_`) pour les services backend, scripts ou outils internes : c'est plus simple et cela donne un accès complet. Utilisez une clé publique (`pk_`) avec JWT pour les applications ou widgets basés sur le navigateur, car cela protège les identifiants et limite l'accès aux domaines autorisés.
