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

# Codes d’erreur

> Gérez les formes d’erreur de passerelle et de ressource renvoyées par l’API v1.

L’API v1 renvoie deux formes d’erreur selon l’endroit où l’échec se produit :

* Les **erreurs de ressource** (erreurs de validation et de ressource introuvable levées dans un endpoint) renvoient un objet **imbriqué** : `{ "error": { "code", "message" } }`. Utilisez `code` pour un traitement par programme.
* Les **erreurs de passerelle**, levées avant l’exécution d’un endpoint, renvoient un objet **plat** : `{ "error", "message" }`. Cela concerne les `401`, les `429` et les `500` de la couche d’authentification. Ici, `error` est un libellé de statut, pas un code exploitable par programme.

Commencez toujours par le statut HTTP, puis lisez le corps selon la forme qui correspond à ce statut.

## Codes de statut HTTP

| Code | Statut | Description |
| - | - | - |
| 200 | OK | Requête réussie |
| 400 | Bad Request | Paramètres de requête invalides |
| 401 | Unauthorized | Authentification absente ou invalide |
| 403 | Forbidden | Authentification valide mais permissions insuffisantes |
| 404 | Not Found | La ressource n’existe pas |
| 429 | Too Many Requests | Limite de débit dépassée |
| 500 | Internal Server Error | Erreur côté serveur |

## Formats des réponses d’erreur

### Erreurs de ressource (imbriquées)

Les erreurs de validation et de ressource introuvable levées dans un endpoint renvoient un objet `error` structuré :

```json theme={null}
{
  "error": {
    "code": "BRAND_NOT_FOUND",
    "message": "Brand not found or not accessible"
  }
}
```

<ResponseField name="error" type="object">
  <Expandable title="Objet error">
    <ResponseField name="code" type="string">
      Code d’erreur exploitable par programme
    </ResponseField>

    <ResponseField name="message" type="string">
      Description lisible de l’erreur
    </ResponseField>
  </Expandable>
</ResponseField>

### Erreurs de passerelle (plates)

Les réponses de passerelle ont une forme plate. `error` est un libellé de statut, pas un code :

```json theme={null}
{
  "error": "Unauthorized",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qw-api-xxx"
}
```

<ResponseField name="error" type="string">
  Court libellé de statut (par exemple `Unauthorized`, `Too Many Requests`).
</ResponseField>

<ResponseField name="message" type="string">
  Description lisible de l’échec.
</ResponseField>

Les réponses 429 incluent en plus `limit` (`"burst"` ou `"daily"`) et `retryAfter` (en secondes).

## Référence des codes d’erreur

### Autorisation

| Code | Statut HTTP | Description |
| - | - | - |
| `INSUFFICIENT_PLAN` | 403 | Offre Growth ou supérieure requise |
| `FORBIDDEN` | 403 | Accès refusé |

Les réponses d’authentification `401` sont plates et ne contiennent pas ces codes de ressource.

### Introuvable

| Code | Statut HTTP | Description |
| - | - | - |
| `BRAND_NOT_FOUND` | 404 | Marque introuvable ou inaccessible |
| `COMPETITOR_NOT_FOUND` | 404 | Concurrent introuvable |
| `SOURCE_NOT_FOUND` | 404 | Source introuvable |
| `PROMPT_NOT_FOUND` | 404 | Prompt introuvable |
| `ANSWER_NOT_FOUND` | 404 | Réponse introuvable |
| `KEYWORD_NOT_FOUND` | 404 | Topic introuvable |
| `TAG_NOT_FOUND` | 404 | Tag introuvable |
| `PITCH_AUDIT_NOT_FOUND` | 404 | Audit de prospection introuvable ou inaccessible |
| `RESOURCE_NOT_FOUND` | 404 | Ressource générique introuvable |
| `ENDPOINT_NOT_FOUND` | 404 | L’endpoint de l’API n’existe pas |

### Requête invalide

| Code | Statut HTTP | Description |
| - | - | - |
| `INVALID_REQUEST` | 400 | Format de requête invalide |
| `INVALID_PARAMETER` | 400 | Valeur de paramètre invalide |
| `MISSING_PARAMETER` | 400 | Paramètre obligatoire manquant |
| `INVALID_DATE_RANGE` | 400 | Plage de dates invalide |
| `INVALID_SORT_FIELD` | 400 | Champ de tri invalide |

### Limitation de débit

Les réponses `429` de la passerelle sont plates. Utilisez le statut HTTP, `limit`, `retryAfter` et l’en-tête `Retry-After` plutôt que d’attendre un code d’erreur imbriqué.

### Erreurs serveur

| Code | Statut HTTP | Description |
| - | - | - |
| `INTERNAL_ERROR` | 500 | Erreur interne du serveur |

## Erreurs courantes

### Erreurs d’authentification (401)

Les échecs d’authentification utilisent la forme **plate** :

```json theme={null}
// Missing or malformed Authorization header
{
  "error": "Unauthorized",
  "message": "Missing or invalid Authorization header. Use: Authorization: Bearer qw-api-xxx"
}

// Invalid token, revoked token, or plan below Growth
{
  "error": "Unauthorized",
  "message": "Invalid API token or insufficient subscription plan (Growth+ required)"
}
```

Vérifiez le préfixe `Bearer`, le statut du jeton, le statut de l’équipe et l’offre active.

### Erreurs de permission (403)

```json theme={null}
// Team subscription doesn't include API access
{
  "error": {
    "code": "INSUFFICIENT_PLAN",
    "message": "Growth plan or higher required"
  }
}
```

Vérifiez la permission propre à la ressource ou la condition d’offre.

### Erreurs de ressource introuvable (404)

```json theme={null}
// Brand doesn't exist
{
  "error": {
    "code": "BRAND_NOT_FOUND",
    "message": "Brand not found or not accessible"
  }
}

// Competitor doesn't exist
{
  "error": {
    "code": "COMPETITOR_NOT_FOUND",
    "message": "Competitor not found"
  }
}
```

Vérifiez que l’identifiant de la ressource appartient à l’équipe associée au jeton.

### Erreurs de limite de débit (429)

Les réponses de limite de débit utilisent la forme **plate** et ajoutent `limit` et `retryAfter` :

```json theme={null}
{
  "error": "Too Many Requests",
  "limit": "burst",
  "message": "Per-minute rate limit exceeded. Please try again in 45 seconds.",
  "retryAfter": 45
}
```

`limit` vaut `"burst"` (par minute) ou `"daily"`.

Respectez l’en-tête `Retry-After` avant de réessayer. Consultez [Limites de débit](/fr/developers/rate-limits).

### Erreurs serveur (500)

```json theme={null}
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred"
  }
}
```

Réessayez les échecs transitoires avec un backoff borné. Si l’erreur persiste, contactez le support en indiquant l’endpoint et l’horodatage de la requête.

## Gérer les erreurs

### Exemple JavaScript/TypeScript

```typescript theme={null}
async function callQwairyAPI(endpoint: string) {
  const response = await fetch(`https://www.qwairy.co/api/v1${endpoint}`, {
    headers: {
      'Authorization': `Bearer ${process.env.QWAIRY_API_TOKEN}`,
    },
  });

  if (!response.ok) {
    const data = await response.json().catch(() => ({}));

    // Gateway errors use the flat shape: { error, message }
    if (response.status === 401) {
      throw new Error(`Authentication failed: ${data.message ?? data.error}`);
    }
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') ?? data.retryAfter;
      throw new Error(`Rate limited. Retry after ${retryAfter} second(s).`);
    }

    // Resource errors use the nested shape: { error: { code, message } }
    const nestedError = typeof data.error === 'object' ? data.error : null;
    const code = nestedError?.code ?? `HTTP_${response.status}`;
    const message = nestedError?.message ?? data.message ?? data.error ?? `HTTP ${response.status}`;

    switch (code) {
      case 'INSUFFICIENT_PLAN':
      case 'FORBIDDEN':
        throw new Error(`Permission denied: ${message}`);
      case 'BRAND_NOT_FOUND':
      case 'COMPETITOR_NOT_FOUND':
      case 'SOURCE_NOT_FOUND':
      case 'PROMPT_NOT_FOUND':
      case 'ANSWER_NOT_FOUND':
        throw new Error(`Resource not found: ${message}`);
      default:
        throw new Error(`API error [${code}]: ${message}`);
    }
  }

  return response.json();
}
```

### Exemple Python

```python theme={null}
import requests
import os

def call_qwairy_api(endpoint):
    response = requests.get(
        f"https://www.qwairy.co/api/v1{endpoint}",
        headers={"Authorization": f"Bearer {os.environ['QWAIRY_API_TOKEN']}"}
    )

    if not response.ok:
        body = response.json() if response.content else {}

        # Gateway errors use the flat shape: { error, message }
        if response.status_code == 401:
            raise Exception(f"Authentication failed: {body.get('message', body.get('error'))}")
        if response.status_code == 429:
            retry_after = response.headers.get('Retry-After', body.get('retryAfter'))
            raise Exception(f"Rate limited. Retry after {retry_after} second(s).")

        # Resource errors use the nested shape: { error: { code, message } }
        error = body.get('error', {}) if isinstance(body.get('error'), dict) else {}
        code = error.get('code', 'UNKNOWN')
        message = error.get('message', body.get('message', 'Unknown error'))

        if code in ['INSUFFICIENT_PLAN', 'FORBIDDEN']:
            raise Exception(f"Permission denied: {message}")
        elif code.endswith('_NOT_FOUND'):
            raise Exception(f"Not found: {message}")
        else:
            raise Exception(f"API error [{code}]: {message}")

    return response.json()
```

## Obtenir de l’aide

En cas d’erreurs persistantes ou de comportement inattendu, écrivez-nous à [team@qwairy.co](mailto:team@qwairy.co) en indiquant :

* l’endpoint appelé
* la réponse d’erreur complète
* les en-têtes de votre requête (sans le jeton)
* l’horodatage de la requête


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.