
# API pour les agences

En tant qu'agence, vous pouvez utiliser la même API REST que vos clients, mais en dirigeant les requêtes individuelles vers l'un de vos **sous-comptes** gérés au lieu de votre propre compte. Cela vous permet de créer des outils qui intègrent un client de bout en bout — création de ses campagnes, entraînement de son IA sur une base de connaissances, importation de ses contacts, connexion de ses canaux de messagerie et achat de numéros de téléphone — le tout sans avoir à vous connecter manuellement à chaque sous-compte.

Cette page couvre uniquement le comportement spécifique aux agences : comment agir au nom d'un sous-compte avec le paramètre `sub_account_id`. Pour les bases (génération de clé, authentification, URL de base, format d'erreur, limites de débit), commencez par le guide [Accès API](../integrations/api-access.md). Tout ce qui y est décrit s'applique également ici : vous vous authentifiez avec la clé API de **votre compte d'agence**.

::: note
**Remarque :** Cette page est technique. Si vous n'êtes pas développeur, partagez-la avec la personne en charge de votre intégration.
:::


***

## Comment fonctionne l'action « au nom de »

Par défaut, chaque requête API agit sur le compte qui possède la clé API, c'est-à-dire votre compte d'agence. Pour agir sur un compte client géré à la place, ajoutez le paramètre optionnel `sub_account_id` à la requête, défini sur l'identifiant de compte de ce client.

- **Omettre `sub_account_id`** → la requête agit sur votre propre compte d'agence.
- **Inclure `sub_account_id`** → la requête agit sur ce sous-compte, mais seulement après que la plateforme a confirmé que le sous-compte vous appartient réellement.

Vous vous authentifiez toujours avec la clé API de **votre compte d'agence**. Vous n'avez jamais besoin de la clé propre au sous-compte et vous ne gérez jamais les identifiants du sous-compte.

### Où l'insérer

- **Points de terminaison GET / DELETE** → passez-le en tant que paramètre de requête : `?sub_account_id=THE_SUB_ACCOUNT_ID` (en plus de votre `apiKey`, si vous vous authentifiez par requête).
- **Points de terminaison POST / PUT / PATCH** → incluez-le dans le corps de la requête JSON en tant que `"sub_account_id": "THE_SUB_ACCOUNT_ID"`.
- **Assistants IA** → rien à configurer. Le [serveur MCP](../integrations/connect-ai-clients.md) applique le même paramètre sur ses outils de lecture, donc une seule connexion avec votre clé d'agence peut générer des rapports sur chaque client : il suffit de nommer le client dans votre requête (« combien de contacts Bella's Bistro possède-t-il ? »). Les actions d'écriture sont également disponibles : chaque point de terminaison qui accepte `sub_account_id` est exposé en tant qu'outil, vous pouvez donc créer, modifier et envoyer au nom d'un client à partir de la même connexion.

### Trouver l'identifiant d'un sous-compte

Le `sub_account_id` est l'identifiant unique du compte client. Vous pouvez obtenir la liste de vos sous-comptes et leurs identifiants à partir des points de terminaison de l'API **SubAccounts** (voir le guide [Sous-comptes](sub-accounts.md)) ou depuis la page **Sous-comptes** dans la barre latérale.

***

## La propriété est toujours vérifiée

Lorsque vous passez un `sub_account_id`, la plateforme vérifie que le compte est un sous-compte réel **et** qu'il appartient à votre agence. Ce n'est qu'à cette condition que la requête est traitée.

Si l'identifiant est inconnu, n'est pas un sous-compte ou appartient à une autre agence, la requête échoue avec une réponse **`404`** :

```json
{
  "success": false,
  "error_code": 404,
  "error": "Sub-account not found."
}
```

> **Pourquoi 404 et non 403 ?** Une réponse « forbidden » indiquerait à un tiers que l'identifiant existe mais ne lui appartient pas. Renvoyer la même `404` pour « n'existe pas » et « ne vous appartient pas » signifie que le point de terminaison ne peut pas être utilisé pour découvrir quels identifiants de compte appartiennent à d'autres agences. Considérez une `404` ici comme « ce n'est pas un sous-compte que vous gérez. »

***

## Où `sub_account_id` est pris en charge

`sub_account_id` est accepté sur pratiquement tous les points de terminaison de **ressource** — tout appel qui crée, lit, met à jour ou supprime les données propres à un compte. En pratique, vous pouvez provisionner et exécuter toute la configuration d'un sous-compte avec votre clé d'agence :

- **Configuration de l'IA** — campagnes, agents, FAQ, sources de base de connaissances (exploration de site web **et** téléchargement de documents), groupes de base de connaissances, diffusions, fonctions personnalisées, serveurs MCP
- **Contacts et CRM** — contacts (y compris l'importation), listes, tags, tâches, opportunités, rendez-vous, événements
- **Canaux et numéros** — connexion WhatsApp / WhatsApp Web / Telegram / Instagram & Messenger / LINE, recherche / achat / gestion de numéros de téléphone, modèles WhatsApp, routage des canaux
- **Messagerie et contenu** — envoi de messages, sessions de chat, exportations de chat, résumés quotidiens
- **Paramètres et intégrations** — webhooks, configuration du widget de chat, configuration en marque blanche, SMS BYOK et autres paramètres de compte, analyses

Pour chacun d'entre eux, le paramètre est **facultatif** — omettez-le et l'appel s'effectuera sur votre compte d'agence, de sorte qu'une seule intégration suffit pour les deux. Les crédits et l'utilisation proviennent toujours du compte ciblé : les frais liés aux campagnes, messages, tags et numéros d'un sous-compte sont débités du solde **du sous-compte**.

### Où cela ne s'applique PAS

Quelques points de terminaison sont au niveau de l'agence ou auto-adressés et ignorent `sub_account_id` :

- **Gestion des sous-comptes eux-mêmes** — les points de terminaison SubAccounts (créer / lister / mettre à jour un sous-compte) et le point de terminaison de limite de dépenses BYOK nomment déjà le sous-compte dans leur propre chemin d'URL. Les [points de terminaison de tarification et de politique](#set-per-client-ai-pricing-and-policy) et les [points de terminaison de surveillance de chat](#read-a-sub-accounts-conversations) suivent le même modèle.
- **Copie d'un agent entre comptes** — `POST /v1/subaccounts/agents/copy` nomme les deux comptes lui-même, en prenant la destination comme `targetUserId`. Voir l'[exemple concret](#worked-example-ship-a-template-agent-into-every-new-client) ci-dessous. (L'ancien `POST /v1/subaccounts/campaigns/copy` fonctionne de la même manière mais est obsolète avec le reste de l'[API Campaigns](../api/campaigns.md).)
- **Ajustement des crédits et les deux cumuls à l'échelle de l'agence** — [`POST /v1/subaccounts/credits`](#grant-or-deduct-credits-directly) identifie le sous-compte par `email` à la place ; [`GET /v1/subaccounts/credit-usage`](#read-credit-usage-and-campaign-health-across-your-book) et `GET /v1/subaccounts/campaign-status` génèrent des rapports sur chaque sous-compte à la fois, il n'y a donc pas de compte unique à cibler.
- **Le propre compte de votre agence** — La gestion des clés API, les rapports d'utilisation de l'agence, la gestion d'équipe et vos [niveaux de tarification](#manage-your-pricing-tiers-over-the-api) agissent toujours sur votre compte d'agence.
- **Webhooks de messages entrants** — les points de terminaison vers lesquels les systèmes externes publient sont liés au compte dont les identifiants les ont configurés, il n'y a donc rien à rediriger.

> La liste toujours à jour et lisible par machine des paramètres acceptés par chaque point de terminaison se trouve dans la référence de l'API de votre tableau de bord (**Paramètres → Intégrations → Clé API**) et dans la spécification OpenAPI à l'adresse `GET /v1/docs/openapi.yaml`. Nous publions fréquemment des modifications de l'API ; considérez-les comme la source de vérité.

::: master-only
<figure><img src="../.gitbook/assets/v2-api-access-key-section.png" alt="Page des paramètres de clé API avec clé masquée et contrôle de régénération"><figcaption><p>Paramètres → Intégrations → Clé API — la clé de votre agence se trouve ici, ainsi que le lien vers la référence complète de l'API.</p></figcaption></figure>
:::

***

## Exemple concret : connecter Instagram et Messenger pour un sous-compte

La connexion à Instagram et Messenger est un flux basé sur le navigateur. Vous le démarrez avec l'API, transmettez l'URL de consentement renvoyée au client (ou ouvrez-la pour lui), attendez qu'il s'autorise dans son navigateur, puis choisissez la page à connecter — tout en ciblant son sous-compte avec `sub_account_id`.

### Étape 1 — Démarrer la connexion

Appelez le point de terminaison de connexion avec le `sub_account_id` du client dans le corps. Aucune information d'identification n'est envoyée ici ; la plateforme renvoie une URL de consentement que le client doit ouvrir dans un navigateur, ainsi qu'un jeton de corrélation à usage unique.

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/channels/meta/connect?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sub_account_id": "abc123def456"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/channels/meta/connect", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
// data.oauth_url -> open this in the client's browser
```

**Python**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/channels/meta/connect",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
# data["oauth_url"] -> open this in the client's browser
```

**Réponse :**

```json
{
  "success": true,
  "oauth_url": "https://www.facebook.com/v21.0/dialog/oauth?client_id=...&state=...",
  "state_token": "8sFq2yV0kQ7m4n1pZr3tWb6cXe9hJl2aD5gK7uN0oI",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Envoyez le client vers `oauth_url` dans un navigateur pour autoriser. Le `state_token` corrèle cette tentative et est un secret à courte durée de vie — ne le consignez pas dans les journaux. La tentative expire à `expires_at` ; si elle expire, recommencez.

### Étape 2 — Interroger jusqu'au chargement des pages

Une fois l'autorisation du client obtenue, interrogez le point de terminaison de statut (avec le même `sub_account_id`, cette fois en tant que paramètre de requête) jusqu'à ce que les pages connectables apparaissent.

**cURL**

```bash
curl "https://api.dmchamp.com/v1/channels/meta/status?apiKey=YOUR_API_KEY&sub_account_id=abc123def456"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.dmchamp.com/v1/channels/meta/status?sub_account_id=abc123def456",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);

const data = await res.json();
// Wait until data.status === "pages_loaded", then read data.pages
```

**Python**

```python
import requests

res = requests.get(
    "https://api.dmchamp.com/v1/channels/meta/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"sub_account_id": "abc123def456"},
)

data = res.json()
# Wait until data["status"] == "pages_loaded", then read data["pages"]
```

**Réponse :**

```json
{
  "success": true,
  "status": "pages_loaded",
  "pages": [
    {
      "id": "1098765432101234",
      "name": "Acme Studio",
      "category": "Hair Salon",
      "instagram_business_account": {
        "id": "17841400000000000",
        "username": "acme.studio"
      }
    }
  ],
  "selected_page": null
}
```

Le champ `status` progresse via `pending` → `token_received` → `pages_loaded` → `connected`. Attendez `pages_loaded` avant de sélectionner une page. Deux états d'erreur terminaux peuvent également apparaître au lieu de progresser : `failed` et `expired` (le client a refusé le consentement, ou la fenêtre d'environ 30 minutes du jeton d'état a expiré) — un champ `reason` est inclus lorsque l'un ou l'autre se produit. Arrêtez l'interrogation et recommencez à l'étape 1 si vous en voyez un ; n'attendez pas indéfiniment sur `pending`. Les jetons d'accès aux pages ne sont jamais renvoyés.

### Étape 3 — Sélectionner la page à connecter

Choisissez l'un des identifiants de page de l'étape 2 et sélectionnez-le. La sélection d'une page connecte à la fois Instagram et Messenger pour cette page. Incluez à nouveau `sub_account_id` dans le corps de la requête.

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/channels/meta/select-page?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "page_id": "1098765432101234",
    "sub_account_id": "abc123def456"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/channels/meta/select-page", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    page_id: "1098765432101234",
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/channels/meta/select-page",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "page_id": "1098765432101234",
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
```

**Réponse :**

```json
{
  "success": true,
  "page_id": "1098765432101234",
  "instagram_business_account_id": "17841400000000000"
}
```

C'est tout — Instagram et Messenger sont maintenant connectés sur le sous-compte du client. Vous n'avez fourni que le `page_id` ; l'identifiant sous-jacent est résolu sur le serveur et ne transite jamais par votre intégration.

***

## Exemple pratique : acheter un numéro pour un sous-compte

L'achat d'un numéro fonctionne de la même manière : effectuez une recherche avec `sub_account_id` dans la requête, puis achetez-le en l'incluant dans le corps. Les crédits sont déduits du solde **du sous-compte**, et le numéro est provisionné sur ce sous-compte.

**Recherche (cURL) :**

```bash
curl "https://api.dmchamp.com/v1/phone-numbers/available?apiKey=YOUR_API_KEY&country_code=US&sub_account_id=abc123def456"
```

**Achat (JavaScript) :**

```javascript
const res = await fetch("https://api.dmchamp.com/v1/phone-numbers", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone_number: "+14155551234",
    country_code: "US",
    display_name: "Support line",
    sub_account_id: "abc123def456",
  }),
});

const data = await res.json();
```

**Achat (Python) :**

```python
import requests

res = requests.post(
    "https://api.dmchamp.com/v1/phone-numbers",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "phone_number": "+14155551234",
        "country_code": "US",
        "display_name": "Support line",
        "sub_account_id": "abc123def456",
    },
)

data = res.json()
```

**Réponse :**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "whatsapp_status": "PURCHASED",
  "outgoing_status": "PURCHASED",
  "status": "PURCHASED",
  "purchase_credits": 11.5,
  "monthly_credits": 11.5
}
```

Le numéro est provisionné dans l'état `PURCHASED` et l'enregistrement de l'expéditeur WhatsApp se poursuit en arrière-plan. Interrogez `GET /v1/phone-numbers/{phoneNumber}/status?sub_account_id=abc123def456` jusqu'à ce que le statut atteigne `ONLINE` avant d'envoyer.

***

## Exemple concret : déployer un agent modèle dans chaque nouveau client

Le modèle d'agence habituel consiste à conserver un agent maître sur votre compte d'agence, configuré comme vous souhaitez que chaque client commence, et à en copier une instance dans chaque nouveau sous-compte au moment du provisionnement. Cela représente trois appels, et rien n'a besoin d'être répété par la suite : la copie conserve ses paramètres jusqu'à ce que vous les modifiiez.

### Étape 1 — Copier l'agent

`POST /v1/subaccounts/agents/copy`

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/agents/copy?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agentId": "YOUR_TEMPLATE_AGENT_ID",
    "targetUserId": "abc123def456",
    "newName": "Inbound Instagram Leads",
    "copyFaqs": true
  }'
```

La réponse contient l'identifiant du nouvel agent dans `data.agent_id`. La FAQ, la base de connaissances et la médiathèque sont incluses ; les modèles WhatsApp, les publications sociales connectées et les contacts du compte source ne le sont délibérément pas. Liste complète des champs dans l'[API AI Agents](../api/agents.md#copy-an-agent-into-a-sub-account-agencies).

Notez que ce point de terminaison utilise `targetUserId` plutôt que `sub_account_id` — il nomme les deux comptes lui-même. Les deux appels ci-dessous utilisent le paramètre `sub_account_id` habituel.

### Étape 2 — L'activer

La copie arrive toujours en pause, elle ne peut donc envoyer de messages à personne tant que vous ne l'avez pas autorisé. C'est également le moment de définir le niveau d'IA que vous souhaitez attribuer au client ; il y reste, il n'est donc pas nécessaire de le réappliquer selon un calendrier.

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agents/NEW_AGENT_ID/active?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "active": true }'

curl -X PUT "https://api.dmchamp.com/v1/agents/NEW_AGENT_ID?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "anthropic_model": "max" }'
```

Pour empêcher le client de modifier le niveau par la suite, [verrouillez les niveaux autorisés](#set-per-client-ai-pricing-and-policy) sur le sous-compte au lieu de renvoyer la valeur.

### Étape 3 — Diriger les canaux du client vers celle-ci

La copie arrive également sans routage, donc rien ne l'atteint tant que vous ne l'avez pas désigné comme répondant sur les canaux que le client a connectés. Un appel par canal :

```bash
curl -X PUT "https://api.dmchamp.com/v1/entry-points/channel-defaults?apiKey=YOUR_API_KEY&sub_account_id=abc123def456" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "instagram", "agent_id": "NEW_AGENT_ID" }'
```

À partir de là, un premier message provenant d'un contact inconnu sur ce canal est automatiquement pris en charge par l'agent copié. Voir [Pointer un canal vers un agent](../api/entry-points.md#point-a-channel-at-an-agent) pour les autres canaux et le routage par numéro.

> **Définissez le fuseau horaire du client lors de la création du sous-compte.** Transmettez `time_zone_id` sur `POST /v1/subaccounts`. Les heures d'activité de la campagne sont évaluées dans le fuseau horaire du sous-compte lui-même, de sorte qu'un client créé sans fuseau horaire voit son planning lu par rapport à l'UTC — ce qui décale silencieusement les moments où l'assistant est autorisé à répondre.

***

## Ignorer l'assistant de configuration pour un client que vous configurez vous-même

`POST /v1/subaccounts`

Par défaut, la première fois qu'un nouveau propriétaire de sous-compte se connecte, il est guidé par l'assistant de configuration. Pour les clients « clé en main » — où vous créez la campagne et connectez les canaux avant même que le client ne se connecte — transmettez `guided_onboarding: false` lors de la création du compte. Ils arrivent directement sur le tableau de bord et l'entrée **Assistant de configuration** est masquée dans leur barre latérale.

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "client@example.com",
    "first_name": "Alex",
    "last_name": "Client",
    "business_name": "Client Co",
    "guided_onboarding": false,
    "usage_limits": { "monthly_credits": 500 }
  }'
```

Omettez le champ (ou envoyez `true`) et l'assistant se comportera exactement comme il l'a toujours fait, de sorte que les intégrations existantes n'ont besoin d'aucune modification. Pour redonner l'assistant à un client plus tard, réaffichez l'élément `guided_onboarding` avec `PUT /v1/subaccounts/{subAccountUid}/menu-visibility` (ci-dessous) — la visibilité du menu contrôle si l'assistant est accessible, `guided_onboarding` contrôle uniquement la redirection lors de la première connexion.

***

## Désactiver les Tâches, les Résumés quotidiens ou la Bibliothèque multimédia pour un client

`POST /v1/subaccounts`

Ces trois fonctionnalités sont activées pour chaque nouveau client, sauf indication contraire de votre part, et elles se comportent différemment de toutes les autres fonctionnalités de ce guide : elles sont en **opt-out** (désactivation volontaire) et non en opt-in. Les omettre de `features` ne suffit pas en soi, car la liste `features` d'une intégration plus ancienne ne les mentionnait tout simplement pas — nous ne pouvons pas distinguer si « l'agence a désactivé ceci » ou si « cette liste a été rédigée avant que l'option n'existe ».

Indiquez-le donc explicitement avec `feature_settings` :

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "client@example.com",
    "first_name": "Alex",
    "last_name": "Client",
    "business_name": "Client Co",
    "feature_settings": {
      "tasks": false,
      "daily_summaries": false,
      "ai_media_library": true
    }
  }'
```

Chaque clé est facultative ; tout ce que vous omettez reste activé. Avec `tasks: false`, l'IA cesse de créer des tâches pour ce client et aucun e-mail de type « Nouvelle tâche créée » n'est envoyé ; avec `daily_summaries: false`, le résumé nocturne n'est jamais généré ni envoyé par e-mail.

`feature_settings` est le seul moyen de désactiver ces trois éléments lors de la création. Les omettre de `features` ne produit aucun effet en soi, quel que soit le reste de votre liste — c'est intentionnel, afin qu'une intégration plus ancienne ne perde pas silencieusement ces trois fonctionnalités.

Pour modifier l'un de ces paramètres par la suite, envoyez la liste complète `features` à `PUT /v1/subaccounts/{subAccountUid}/features` — ici, la présence dans la liste active une fonctionnalité et son absence la désactive.

***

## Connectez automatiquement vos clients à leur sous-compte (SSO)

`POST /v1/subaccounts/{subAccountUid}/sso-link`

Un seul appel avec votre clé API d'agence renvoie une URL prête à l'emploi qui connecte directement le client à son propre sous-compte — aucun écran de connexion, aucune étape de mot de passe, rien à développer en plus. Ouvrez-la dans un nouvel onglet, via une redirection ou dans une iframe au sein de votre propre produit.

| Champ | Requis | Description |
|---|---|---|
| `redirect` | Non | Page intégrée à l'application sur laquelle vous souhaitez que le client aboutisse, par ex. `"/chats"` ou `"/agents"`. Renvoyé sous le nom `deep_link_url` dans la réponse. |
| `app_base_url` | Non | Hôte du tableau de bord pour le lien. Utilise par défaut votre domaine d'application en marque blanche (ou le domaine de la plateforme si vous n'en avez pas). Doit être `https`. |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/sso-link" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "redirect": "/chats" }'
```

**Réponse**

```json
{
  "success": true,
  "url": "https://app.yourdomain.com/auth?redirect=%2Fchats#token=eyJhbGciOi…",
  "deep_link_url": "https://app.yourdomain.com/chats",
  "expires_at": "2026-07-22T15:04:05.000Z",
  "sub_account_uid": "SUB_ACCOUNT_UID"
}
```

Comment bien l'utiliser :

- **Un seul saut.** L'ouverture de `url` connecte le client et l'envoie directement sur la page `redirect` du tableau de bord — pas d'écran de connexion, pas de page intermédiaire. `deep_link_url` désigne la même destination, pour les intégrateurs qui préfèrent naviguer explicitement dans un cadre après la connexion ; une fois la session établie, n'importe quel chemin du tableau de bord fonctionne dans ce contexte de navigateur.
- **Création à la demande, ouverture immédiate.** Le lien contient un identifiant de connexion et expire après environ une heure. Demandez-le côté serveur au moment où le client clique, et ne le stockez ni ne l'envoyez jamais par e-mail.
- Le jeton de connexion transite dans le fragment d'URL (`#…`), que les navigateurs n'envoient jamais aux serveurs, et il est supprimé de la barre d'adresse dès qu'il est consommé.
- **Uniquement vos propres sous-comptes.** Le point de terminaison refuse tout compte qui n'appartient pas à votre agence.
- Un lien expiré affiche une erreur claire avec un chemin de réessai — générez-en un nouveau.

***

## Masquer des éléments de navigation sur un sous-compte

`PUT /v1/subaccounts/{subAccountUid}/menu-visibility`

Contrôle les éléments de la barre latérale et des paramètres visibles par un sous-compte — utile lorsque vous intégrez le tableau de bord et que vous souhaitez uniquement afficher les surfaces que votre produit ne couvre pas déjà. Tout ce qui n'est pas listé reste visible ; envoyez `null` comme valeur entière de `menuVisibility` pour tout réinitialiser en mode visible. Masquer un élément masque l'entrée du menu — associez cela aux fonctionnalités que vous accordez au sous-compte pour un contrôle strict.

**cURL**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/menu-visibility" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "menuVisibility": {
      "side_nav": { "Dashboard": false, "Campaigns": false, "Automations": false },
      "settings_nav": { "team": false }
    }
  }'
```

**Réponse**

```json
{
  "success": true,
  "data": {
    "subAccountUid": "SUB_ACCOUNT_UID",
    "menuVisibility": {
      "side_nav": { "Dashboard": false, "Campaigns": false, "Automations": false },
      "settings_nav": { "team": false }
    }
  }
}
```

`side_nav` accepte ces 13 clés, qui correspondent aux noms des éléments de la barre latérale : `Dashboard`, `DailySummaries`, `Chats`, `Contacts`, `Deals`, `Tasks`, `Automations`, `Campaigns`, `Appointments`, `Settings`, `Help`, `CreditsCounter` (le solde de crédit affiché dans la barre latérale) et `guided_onboarding` (l'assistant de configuration). Trois autres clés — `AiInsights`, `Sub Accounts` et `Agency Reselling` — sont acceptées mais ne font rien : elles ne s'appliquaient qu'à l'ancien tableau de bord classique, leur définition n'a donc aucun effet sur vos sous-comptes. Les clés manquantes signifient visible ; lorsque vous vous connectez vous-même au sous-compte, les éléments masqués sont temporairement affichés afin que vous puissiez toujours revenir en arrière.

Masquer une page du menu n'en accorde jamais l'accès. `Automations` nécessite que la fonctionnalité `automations` soit accordée sur le sous-compte — définissez la clé sur `true` sans cela et la page n'apparaîtra toujours pas. `Tasks` et `DailySummaries` fonctionnent à l'inverse : elles sont activées pour chaque client à moins que vous ne les désactiviez (voir [Désactiver les Tâches, les Résumés quotidiens ou la Bibliothèque multimédia pour un client](#turn-tasks-daily-summaries-or-the-media-library-off-for-a-client)).

***

## Choisir les types de canaux auxquels un client peut se connecter

`PUT /v1/subaccounts/{subAccountUid}/features`

Les commutateurs **Types de canaux** que vous voyez sur un niveau de plan sont des identifiants de fonctionnalité ordinaires. Vous pouvez donc les définir par client depuis l'API au lieu du tableau de bord. Il s'agit de l'un des points de terminaison qui nomme le sous-compte dans sa propre URL, il ne nécessite donc aucun `sub_account_id`.

| ID de fonctionnalité | Canal |
|---|---|
| `channel_chat_widget` | Widget de chat sur site web |
| `channel_whatsapp_api` | API WhatsApp Business |
| `channel_whatsapp_web` | WhatsApp Web (numéro lié par QR) |
| `channel_instagram` | Instagram |
| `channel_messenger` | Facebook Messenger |
| `channel_telegram` | Telegram |
| `channel_line` | LINE |
| `channel_viber` | Viber |
| `channel_email` | Boîte de réception e-mail |
| `channel_sms` | SMS |
| `channel_imessage` | iMessage |

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/features" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "features": [
      "channels_3",
      "channel_chat_widget",
      "channel_whatsapp_web",
      "channel_instagram",
      "image_understanding",
      "contact_tagging",
      "incoming_campaigns",
      "webhooks"
    ]
  }'
```

Trois points à bien comprendre :

- **L'appel remplace toute la liste des fonctionnalités.** Envoyez toutes les fonctionnalités que le client doit conserver, pas seulement celles que vous modifiez. Les mêmes identifiants fonctionnent comme `features` sur `POST /v1/subaccounts` lorsque vous créez le compte.
- **Les types de canaux et le nombre de canaux sont des verrous distincts, et les deux s'appliquent.** `channels_1` / `channels_3` / `channels_unlimited` contrôlent *combien* de connexions sont autorisées ; les identifiants `channel_*` contrôlent *quels types*. L'exemple ci-dessus signifie « jusqu'à 3 connexions, et uniquement le widget de chat, WhatsApp Web ou Instagram ».
- **Ne pas envoyer d'identifiants `channel_*` signifie qu'il n'y a aucune restriction de canal.** C'est le comportement d'origine, c'est pourquoi les clients existants n'ont pas été affectés lors du déploiement de cette fonctionnalité. Envoyez-en un ou plusieurs et tout le reste apparaîtra comme verrouillé sur la page Canaux du client avec une note de mise à niveau au lieu d'un bouton Connecter. Les canaux déjà connectés par le client continuent de fonctionner.

> La définition de la liste des canaux sur un **niveau de plan**, afin que chaque client qui achète ce niveau en hérite, s'effectue dans le tableau de bord sous les paramètres de votre plan d'agence. Ce point de terminaison la définit pour un sous-compte spécifique.

***

## Définir une limite exacte de membres d'équipe pour un client

`PUT /v1/subaccounts/{subAccountUid}/limits`

Les fonctionnalités `team_seats_*` ne proposent que des paliers prédéfinis (3 / 5 / 10 / illimité). Pour attribuer à un client un nombre **exact** de sièges d'équipe — 2, 7, 15, ou tout autre nombre — définissez plutôt `usage_limits.team_seats_limit`. Cette valeur prévaut sur les paliers prédéfinis et la plateforme l'applique à chaque invitation, ajout direct et acceptation d'invitation : une fois la limite atteinte, les invitations supplémentaires sont refusées côté serveur.

- Un entier positif correspond au plafond exact.
- `0` signifie que les membres d'équipe ne sont **pas inclus** — le client ne peut inviter personne.
- `-1` signifie illimité.
- `null` efface la limite personnalisée et revient au palier `team_seats_*` défini dans la liste des fonctionnalités.

Réduire la limite ne supprime jamais les membres d'équipe existants ; cela empêche seulement l'ajout de nouveaux membres.

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/limits" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "usageLimits": { "team_seats_limit": 7 }
  }'
```

Vous pouvez également le définir lors de la création : `POST /v1/subaccounts` accepte `usage_limits.team_seats_limit` avec la même sémantique. Pour lire la valeur actuelle, récupérez le sous-compte avec `GET /v1/subaccounts?email=...` et examinez `usage_limits.team_seats_limit` (absent/`null` = les préréglages décident). Le même point de terminaison met également à jour `credits`, `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months`, `rollover_expiry_days` et `byok_monthly_limit_usd` — envoyez uniquement les clés que vous souhaitez modifier.

**Comment cela interagit avec les limites de sièges des plans SaaS.** Vos plans SaaS peuvent comporter leur propre allocation de sièges (définie dans l'éditeur de plan — voir [Sièges d'équipe sur un plan](agency-accounts.md#step-3--set-up-pricing-tiers)), qui est appliquée automatiquement lorsqu'un client s'abonne. Une limite que vous définissez via ce point de terminaison compte comme une attribution **manuelle** : l'achat d'un plan la remplace par l'allocation de sièges propre au plan (cet achat étant un choix de plan explicite), mais les **renouvellements** mensuels automatiques **ne remplacent jamais une limite manuelle** — ainsi, une exception ponctuelle que vous accordez à un client survit à son cycle de facturation. Supprimer la limite manuelle avec `null` redonne la main au plan lors de son prochain renouvellement.

**Limitez ce qu'un client conserve entre deux renouvellements.** Deux clés `usage_limits` supplémentaires se trouvent à côté de `roll_over_to_next_month`. Toutes deux sont également acceptées par `POST /v1/subaccounts` lors de la création, et `null` efface l'une ou l'autre.

| Clé | Action |
|---|---|
| `rollover_cap_months` | Nombre de mois d'allocation que le client peut conserver. Un nombre de 0 à 120, fractions autorisées (`0.5` = un demi-mois). À chaque renouvellement, le solde inutilisé est réduit au maximum à ce nombre de fois l'allocation accordée par ce renouvellement, avant que les nouveaux crédits ne soient ajoutés ; `0` ne reporte rien. |
| `rollover_expiry_days` | Un nombre entier de jours, de 1 à 3650. Les crédits inutilisés pendant cette durée sont supprimés lors du premier renouvellement après avoir atteint cet âge. La consommation est toujours déduite des crédits les plus anciens en premier, donc un client qui utilise son allocation chaque mois n'en perd jamais. |

S'ils ne sont pas définis, les deux reviennent au plan du client ; une valeur envoyée ici prévaut sur celle du plan. Seuls les crédits récurrents (l'allocation mensuelle et les crédits du plan) y sont soumis : les recharges, les recharges automatiques et les ajouts ponctuels ne sont jamais plafonnés ni expirés. Chaque réduction est inscrite dans l'historique des crédits du client en tant qu'**Ajustement de crédit de plafond de report** ou **Ajustement de crédit de crédits expirés** et ne compte jamais comme une utilisation. Les équivalents au niveau du plan sont `rollover_cap_months` et `rollover_expiry_days` sur un niveau de tarification — voir [Les champs sur un niveau](#the-fields-on-a-tier) et [Plafonner ce qui est reporté](sub-accounts.md#capping-what-rolls-over).

***

## Définir la tarification et la politique IA par client

`PUT /v1/subaccounts/{subAccountUid}/max-tier` · `/ai-tiers` · `/max-rate` · `/action-pricing` · `/insider-rate` · `/locked-bot-fields` · `/notifications` · `/zero-credit-reply`

Huit commutateurs supplémentaires par client, en plus de `/limits`, `/features` et `/menu-visibility` ci-dessus. Chacun prend l'uid du sous-compte dans l'URL (aucun paramètre de corps/requête `sub_account_id` — la cible est déjà nommée dans le chemin) et est limité de la même manière : votre clé d'agence, et le sous-compte doit appartenir à votre agence.

**Quels modèles d'IA un client peut utiliser**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/max-tier" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

`{ "enabled": boolean }` permet au client d'opter pour (ou de sortir de) le niveau Max AI — notre infrastructure au prix catalogue de la plateforme. L'activation de cette option pour un client BYOK fait passer son coût d'IA de « gratuit sur ma propre clé » à « facturé sur mon pool de crédits », il s'agit donc d'une décision délibérée par client plutôt que d'une valeur par défaut à l'échelle de l'agence.

Pour restreindre les niveaux que les campagnes et les agents d'un client peuvent choisir (plutôt que de simplement limiter Max), utilisez `ai-tiers` :

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/ai-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "allowed_ai_tiers": ["standard", "economy"] }'
```

`allowed_ai_tiers` est un tableau tiré de `standard`, `economy`, `max`, `mini` — il REMPLACE la liste d'autorisation du client. Envoyez `null` (ou `[]`) pour effacer la restriction et leur permettre de choisir n'importe quel niveau. Ceci est important car un sous-compte choisissant son propre niveau d'IA dépense à partir de **votre** pool de crédits, c'est donc le levier pour déterminer quels modèles un client revendeur peut utiliser pour augmenter votre facture.

**Une réponse d'attente lorsque le client n'a plus de crédits**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/zero-credit-reply" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "message": "Thanks for your message, we will get back to you shortly." }'
```

Lorsque le solde du client (ou votre réserve) est vide, l'IA ne peut pas répondre et le contact n'entend rien. Avec `enabled: true`, chaque contact qui écrit pendant la panne reçoit `message` une fois (max 500 caractères, envoyé tel quel sur chaque canal), et l'IA répond réellement à ces conversations une fois que les crédits sont de retour. `enabled: false` conserve le texte enregistré pour plus tard ; `enabled: false` sans `message` supprime le paramètre. Même commutateur que **Réponse d'attente en cas de manque de crédits** dans la fenêtre modale de modification du sous-compte — voir [Une réponse d'attente lorsqu'un client n'a plus de crédits](sub-accounts.md#a-holding-reply-while-a-client-is-out-of-credits).

**Ce qu'un client paie par action IA, et la majoration des frais WhatsApp**

Deux façons de définir votre tarif client, du plus simple au plus granulaire :

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/max-rate" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "rate": 0.35 }'
```

`rate` est le prix en crédits que le solde PROPRE du sous-compte brûle par action IA de modèle Max — votre majoration côté client en plus de ce que votre pool paie réellement. `null` réinitialise la surcharge au prix catalogue de la plateforme. Le taux doit être au moins égal au coût d'une action Max pour votre propre pool (vous ne pouvez donc jamais facturer un client en dessous de votre coût) et ne pas dépasser 10 crédits ; une requête en dehors de cette fenêtre est rejetée avec le plancher calculé dans le message d'erreur.

Pour une tarification par type d'action au lieu d'un tarif Max forfaitaire, utilisez `action-pricing` :

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/action-pricing" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "actionPricing": {
      "AI_MESSAGE": 0.6,
      "CHAT_SUMMARY": 0.15,
      "wa_carrier_multiplier": null
    }
  }'
```

`actionPricing` est une FUSION sur la carte existante du client — une clé que vous ne mentionnez pas est laissée telle quelle, et `null` réinitialise cette clé à sa valeur par défaut. Les clés reconnues :

| Clé | Prix |
|---|---|
| `AI_MESSAGE` | Une réponse IA |
| `AI_TOOL_USE` | Un appel d'outil IA |
| `EVALUATION_CALL` | Une passe d'évaluation de chat |
| `INTERRUPTION_HANDLING` | Gestion d'une interruption en milieu de réponse |
| `CONTACT_TAG` | Une étiquette de contact attribuée par l'IA |
| `CHAT_SUMMARY` | Un résumé de chat |
| `wa_carrier_multiplier` | Un multiplicateur de majoration appliqué à chaque frais WhatsApp non-IA que le client paie : location mensuelle de numéro, frais de livraison sur voie gérée et coûts de transfert des modèles Meta/Twilio. |

Les tarifs par action doivent être un nombre supérieur à 0 et allant jusqu'à 10 ; `wa_carrier_multiplier` doit être d'au moins `1` (pas de remise en dessous du coût) et jusqu'à 10. L'envoi d'une clé non reconnue, ou d'une valeur hors limites, rejette la TOTALITÉ de la requête et nomme chaque clé fautive, afin qu'une faute de frappe ne puisse jamais enregistrer silencieusement un prix qui n'est pas réellement appliqué.

Si vous êtes membre du [Champions Circle](https://skool.com/dm-champions), `insider-rate` transmet votre tarif de 20 % de réduction Max/Lead Finder à un seul client au lieu de l'appliquer à l'ensemble de l'agence :

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/insider-rate" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

L'activation nécessite que votre propre compte d'agence détienne réellement une adhésion au Circle ; la désactivation ne le nécessite jamais, donc un membre dont l'adhésion a expiré peut toujours revenir en arrière pour un client.

**Verrouiller des sections du playbook d'un client**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/locked-bot-fields" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "locked_bot_fields": ["instructions", "rules"] }'
```

`locked_bot_fields` est un tableau tiré de `instructions`, `goal`, `rules`, `personality`, `conclude_unless` — il REMPLACE la liste verrouillée du client. Une section verrouillée est rejetée côté serveur si le SOUS-COMPTE lui-même tente de la modifier (directement ou par clé API), tandis que vous (via `sub_account_id`) et la vue administrateur du tableau de bord du client pouvez toujours tout modifier. Envoyez `null` (ou `[]`) pour tout déverrouiller. Utile pour les clients « clé en main » où vous possédez le playbook et êtes jugé sur le résultat.

**Définir les préférences de notification d'un client en son nom**

```bash
curl -X PUT "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/notifications" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "notifications": {
      "settings": {
        "credit_alerts": { "enabled": true, "channels": ["email", "in_app"] },
        "new_contacts": { "enabled": false }
      }
    }
  }'
```

`notifications` remplace l'ensemble des préférences de notification du client (pas une fusion par clé — envoyez chaque catégorie que vous souhaitez conserver, en correspondant à la façon dont la page Paramètres du sous-compte enregistre les données). Chaque catégorie sous `settings` accepte `enabled` (booléen) et jusqu'à trois `channels` parmi `email`, `in_app`, `webhook`. Envoyez `null` pour réinitialiser aux valeurs par défaut de la plateforme.

Les sept points de terminaison répondent `{ "success": true, "data": { "subAccountUid": "...", ...the field(s) you set... } }` et sont consignés dans le journal d'audit avec la valeur avant/après. Erreurs courantes : `403` si votre compte n'est pas de type Agence/Dev ou si le sous-compte n'est pas sous votre gestion, `400` s'il ne s'agit pas d'un sous-compte d'agence ou si une valeur est hors limites.

***

## Suspendre un client ayant interrompu son abonnement

`POST /v1/subaccounts/{subAccountUid}/pause` · `POST /v1/subaccounts/{subAccountUid}/unpause`

Lorsqu'un client suspend son abonnement auprès de vous, mettez son compte en pause au lieu de le supprimer : tout ce qu'il envoie s'arrête immédiatement — messages sortants, diffusions, réponses IA sur tous les canaux — et lorsqu'il se connecte, il voit un écran de verrouillage **Compte suspendu** (avec votre message optionnel) au lieu de l'application. Rien n'est supprimé ou déconnecté : les agents, les campagnes, les canaux connectés, les contacts et l'historique des discussions restent exactement tels quels, de sorte que la réactivation remet le client précisément là où il s'était arrêté — aucune configuration à refaire.

| Champ | Requis | Description |
|---|---|---|
| `message` | Non | Affiché au client sur son écran de verrouillage. Laissez vide pour utiliser le libellé par défaut. |
| `reason` | Non | Note interne à l'agence stockée avec la mise en pause et dans le journal d'audit — jamais montrée au client. |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/pause" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Your account is on hold — contact us to reactivate it.", "reason": "Subscription suspended per client email" }'
```

**Réponse**

```json
{
  "success": true,
  "data": {
    "subAccountUid": "SUB_ACCOUNT_UID",
    "paused": true,
    "level": "hard_blocked"
  }
}
```

Lorsque le client revient, `POST /v1/subaccounts/SUB_ACCOUNT_UID/unpause` (sans corps) lève le verrouillage — l'envoi et les réponses IA reprennent immédiatement.

Bon à savoir :

- **Il s'agit du même état que le bouton de blocage strict du tableau de bord** ([Bloquer / Mettre en pause un sous-compte](sub-accounts.md#blocking-pausing-a-sub-account)) — un client mis en pause via l'API apparaît comme bloqué dans le tableau de bord et vice versa, et la réactivation annule un blocage effectué de l'un ou l'autre côté. L'état actuel est lisible depuis le champ `agency_block` sur `GET /v1/subaccounts` (`level` de `"none"`, `"soft_blocked"` ou `"hard_blocked"`).
- **Les deux appels sont idempotents.** Mettre en pause un client déjà en pause ne fait que rafraîchir le message, la raison et l'horodatage ; réactiver un client actif ne change rien.
- **Le client n'est pas informé automatiquement par e-mail** — de nombreuses agences utilisant la marque blanche, c'est à vous de prévenir le client.
- **Votre propre facturation DM Champ reste inchangée.** Mettre un client en pause n'affecte que votre relation avec lui.
- **Les assistants IA peuvent également le faire** : le [serveur MCP](../integrations/connect-ai-clients.md) expose ces points de terminaison en tant qu'outils `pause_subaccount` et `unpause_subaccount`.

***

## Accorder ou déduire des crédits directement

`POST /v1/subaccounts/credits`

Ajoute ou supprime un montant exact de crédits du solde d'un sous-compte — l'équivalent API de l'ajustement manuel des crédits du tableau de bord. Il s'agit d'une modification de solde ponctuelle, distincte des paramètres récurrents `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months` et `rollover_expiry_days` sur [`PUT /v1/subaccounts/{subAccountUid}/limits`](#set-an-exact-team-member-limit-for-a-client).

C'est le seul point de terminaison sur cette page qui identifie le sous-compte par **e-mail** plutôt que par `sub_account_id`.

| Champ | Requis | Description |
|---|---|---|
| `email` | Oui | L'e-mail du sous-compte, tel qu'il existe sous votre agence. |
| `amount` | Oui | Nombre de crédits non nul. Positif pour ajouter, négatif pour déduire. |
| `description` | Non | Affiché à côté de l'ajustement dans l'historique des crédits du client. Par défaut, une ligne générique « Ajusté par l'agence via API ». |

**cURL**

```bash
curl -X POST "https://api.dmchamp.com/v1/subaccounts/credits" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "client@example.com", "amount": 500, "description": "Q3 bonus credits" }'
```

**Réponse**

```json
{
  "success": true,
  "data": {
    "email": "client@example.com",
    "previous_balance": 1200,
    "adjustment": 500,
    "new_balance": 1700
  }
}
```

Un `amount` négatif qui ferait passer le solde en dessous de zéro est refusé avec `400`, vous indiquant le solde disponible et ce que vous avez tenté de déduire. Si le client utilise sa propre facturation Stripe (mode revendeur), un montant ajouté compte également comme des crédits qu'il a achetés, il survit donc à sa prochaine réinitialisation mensuelle de la même manière qu'une vraie recharge ; sur un client alloué standard, il est traité comme faisant partie de son allocation récurrente à la place. Dans les deux cas, il s'agit d'un ajout ponctuel, donc un plafond de report ou une expiration définie sur le compte (ou son plan) ne les réduit jamais — seuls l'allocation récurrente et les crédits du plan y sont soumis.

***

## Lire les conversations d'un sous-compte

`GET /v1/subaccounts/{subAccountUid}/chats` · `GET /v1/subaccounts/{subAccountUid}/chats/{contactId}/messages`

Vous permet de créer une vue de surveillance ou de support des conversations d'un client sans vous connecter à son compte. Listez d'abord ses contacts avec un aperçu du dernier message, puis lisez l'historique complet des messages d'un contact.

**Lister les contacts**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/chats?apiKey=YOUR_AGENCY_API_KEY&pageSize=25"
```

| Paramètre de requête | Requis | Description |
|---|---|---|
| `pageSize` | Non | Contacts par page. 25 par défaut, 50 maximum. |
| `lastActivityAt` | Non | Curseur de pagination — transmettez le `lastActivityAt` de la page précédente pour continuer. |
| `searchQuery` | Non | Filtrer par nom de contact ou numéro de téléphone. |

**Réponse**

```json
{
  "success": true,
  "data": {
    "contacts": [
      {
        "contactId": "contact456",
        "firstName": "Jamie",
        "lastName": "Lee",
        "phoneNumber": "+14155551234",
        "email": "jamie@example.com",
        "channel": "whatsapp",
        "lastActivityAt": "2026-08-30T14:22:00.000Z",
        "lastMessage": { "body": "Thanks, that fixed it!", "direction": "inbound", "timestamp": "2026-08-30T14:22:00.000Z" },
        "isBotActive": true,
        "markChatClosed": false
      }
    ],
    "subAccountName": "Client Co",
    "subAccountEmail": "client@example.com",
    "hasMore": true,
    "lastActivityAt": "2026-08-30T14:22:00.000Z"
  }
}
```

Les contacts sont triés par activité la plus récente en premier. Continuez la pagination avec `lastActivityAt` tant que `hasMore` est `true`.

**Lire les messages d'un contact**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/SUB_ACCOUNT_UID/chats/contact456/messages?apiKey=YOUR_AGENCY_API_KEY&pageSize=30"
```

| Paramètre de requête | Requis | Description |
|---|---|---|
| `pageSize` | Non | Messages par page. 30 par défaut, 100 maximum. |
| `beforeTimestamp` | Non | Curseur de pagination — récupérez les messages antérieurs à cet horodatage ISO. |

**Réponse**

```json
{
  "success": true,
  "data": {
    "messages": [
      {
        "messageId": "msg789",
        "body": "Thanks, that fixed it!",
        "direction": "inbound",
        "timestamp": "2026-08-30T14:22:00.000Z",
        "status": "received",
        "channel": "whatsapp",
        "botReply": false,
        "mediaUrl": null,
        "mediaContentType": null,
        "name": "Jamie Lee",
        "role": null
      }
    ],
    "contactInfo": { "firstName": "Jamie", "lastName": "Lee", "phoneNumber": "+14155551234", "channel": "whatsapp" },
    "hasMore": false,
    "oldestTimestamp": "2026-08-30T14:22:00.000Z"
  }
}
```

Les messages sont renvoyés du plus récent au plus ancien ; parcourez l'historique page par page avec `beforeTimestamp`.

***

## Lire l'utilisation des crédits et l'état des campagnes pour l'ensemble de votre portefeuille

`GET /v1/subaccounts/credit-usage` · `GET /v1/subaccounts/campaign-status`

Deux synthèses de type tableau de bord pour tous les sous-comptes que vous gérez, afin de créer vos propres rapports d'agence au lieu de cliquer sur chaque client un par un.

**Utilisation des crédits**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/credit-usage?apiKey=YOUR_AGENCY_API_KEY&from=2026-08-01&to=2026-08-31"
```

| Paramètre de requête | Requis | Description |
|---|---|---|
| `from` / `to` | Oui | Plage de dates ISO. |
| `subAccountId` | Non | Omettez pour un résumé à l'échelle de l'agence, une ligne par sous-compte. Incluez pour passer en mode détail : le résumé de ce sous-compte ainsi que ses enregistrements d'utilisation bruts et paginés. |
| `limitCount` | Non | Mode détail uniquement. 500 par défaut, 2000 maximum. |
| `startAfterTimestamp` | Non | Mode détail uniquement — curseur de pagination. |

```json
{
  "success": true,
  "data": {
    "subAccounts": [
      {
        "subAccountId": "abc123def456",
        "subAccountName": "Client Co",
        "subAccountEmail": "client@example.com",
        "totalCreditsUsed": 842,
        "totalCostUsd": 3.15,
        "byReason": { "AI reply": 620, "Chat summary": 80 },
        "topCampaigns": [{ "campaignName": "Inbound Leads", "creditsUsed": 500 }]
      }
    ],
    "totals": { "totalCreditsUsed": 842, "totalCostUsd": 3.15, "totalRecords": 214 },
    "dateRange": { "from": "2026-08-01", "to": "2026-08-31" },
    "hasMore": false,
    "lastTimestamp": null
  }
}
```

Transmettez `subAccountId` et la réponse contiendra également `records` : les frais individuels avec `amount`, `reason`, `campaignName`, `contactName` et `timestamp`. Un client utilisant sa propre clé BYOK plutôt que vos crédits verra ses chiffres de coût/jeton masqués (`costsRedacted: true`) — il s'agit de télémétrie des coûts de la plateforme, et non d'informations à afficher à un revendeur. |

**État de la campagne**

```bash
curl "https://api.dmchamp.com/v1/subaccounts/campaign-status?apiKey=YOUR_AGENCY_API_KEY&pageSize=20"
```

| Paramètre de requête | Requis | Description |
|---|---|---|
| `pageSize` | Non | Sous-comptes par page. Par défaut 10, maximum 50. |
| `lastDocumentId` | Non | Curseur de pagination. |
| `searchQuery` | Non | Filtrer par nom ou e-mail de sous-compte. |

```json
{
  "success": true,
  "data": {
    "totalSubAccounts": 34,
    "subAccountsWithIssues": 3,
    "totalLiveCampaigns": 51,
    "totalPausedCampaigns": 6,
    "subAccounts": [
      {
        "userId": "abc123def456",
        "email": "client@example.com",
        "displayName": "Jamie Lee",
        "businessName": "Client Co",
        "totalCampaigns": 2,
        "liveCampaigns": 1,
        "pausedCampaigns": 1,
        "hasIssues": true,
        "issueDetails": ["1 campaign paused"],
        "lastCampaignActivity": "2026-08-29T09:00:00.000Z"
      }
    ],
    "hasMore": true,
    "lastDocumentId": "abc123def456",
    "pageSize": 20
  }
}
```

`hasIssues` / `issueDetails` signalent les sous-comptes qui méritent une attention particulière — une campagne en pause, ou une campagne sans canal associé, par exemple. Utilisez ceci pour créer un tableau de bord de contrôle de santé pour l'ensemble du portefeuille plutôt que d'ouvrir chaque client pour remarquer une campagne bloquée.

> Pour la messagerie chronologique et l'activité de crédit pour chaque client (une série prête à être mise en graphique plutôt qu'un instantané ponctuel), voir `GET /analytics/agency-rollup` dans le guide de l'API Analytics.

***

## Déployez un compte client déjà configuré

`PUT /v1/snapshots/default` · `POST /v1/snapshots/{snapshotId}/apply`

Un [instantané](snapshots.md) est un modèle réutilisable : un ou plusieurs agents IA ainsi que leur base de connaissances, leurs outils et leurs médias, capturés depuis votre propre compte. Deux points de terminaison permettent de l'intégrer à votre flux de provisionnement.

**Automatique — chaque nouveau client en est doté dès sa création.** Marquez un instantané comme étant votre modèle par défaut une seule fois, et chaque compte que vous créerez par la suite sera installé avec celui-ci. Cela couvre les comptes créés via `POST /v1/subaccounts`, les comptes que vous créez dans le tableau de bord, et les comptes créés automatiquement lorsqu'un client paie via votre lien de paiement.

Tout d'abord, trouvez l'identifiant de l'instantané :

```bash
curl "https://api.dmchamp.com/v1/snapshots" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

Ensuite, définissez-le comme étant le modèle par défaut :

```bash
curl -X PUT "https://api.dmchamp.com/v1/snapshots/default" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "snapshot_id": "SNAPSHOT_ID" }'
```

C'est tout pour l'intégration. Envoyez `{"snapshot_id": null}` pour le désactiver à nouveau. Vous pouvez faire la même chose depuis le tableau de bord en cliquant sur l'étoile sur la page **Instantanés**.

Pour lire ce qui est actuellement mis en favori (par exemple, avant qu'un script de provisionnement ne décide s'il faut en définir un), `GET /v1/snapshots/default` renvoie `{ "success": true, "data": { "default_snapshot_id": "SNAPSHOT_ID" } }` — `null` quand rien n'est mis en favori. `GET /v1/snapshots` (utilisé pour trouver l'id ci-dessus) renvoie le même `default_snapshot_id` avec le tableau complet `snapshots`, donc la plupart des intégrations n'ont besoin que de cet appel. Les champs complets de l'objet instantané se trouvent dans le guide [Snapshots](snapshots.md).

**À la demande — installation dans un compte spécifique.** Utile pour l'intégration d'un client existant, ou pour fournir un second modèle à un client ultérieurement.

```bash
curl -X POST "https://api.dmchamp.com/v1/snapshots/SNAPSHOT_ID/apply" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sub_account_id": "SUB_ACCOUNT_UID" }'
```

Omettez `sub_account_id` et il s'installera dans votre propre compte d'agence à la place. Comme pour les points de terminaison `/subaccounts`, ceux-ci nomment le compte cible dans le chemin ou le corps de la requête plutôt que via le paramètre ambiant `sub_account_id`.

Bon à savoir avant de construire dessus :

- **Les agents installés démarrent en pause.** Connectez d'abord les canaux du client, puis activez l'agent. Cela est vrai pour le chemin automatique comme pour le chemin à la demande.
- **Le provisionnement n'échoue jamais à cause d'un instantané.** Si l'installation ne peut pas aboutir, le compte client est tout de même créé et utilisable — il arrive simplement vide et vous pouvez appliquer l'instantané par la suite.
- **Les canaux, calendriers et connexions OAuth ne sont jamais copiés.** Chaque compte connecte les siens. Les outils utilisant une clé API simple continuent de fonctionner immédiatement.
- **Appliquer deux fois crée une seconde copie.** Rien n'est écrasé.

***

## Créer le modèle lui-même via l'API

`POST /v1/snapshots` · agents, fonctions personnalisées et médias via l'API

La section ci-dessus distribue un instantané créé par quelqu'un dans le tableau de bord. La partie création est également exposée, de sorte que la boucle complète — assembler la configuration principale une fois, la capturer, la transmettre à chaque client — peut être exécutée à partir du code.

Les éléments, dans l'ordre où un script de provisionnement les utilise :

1. **Créez vos fonctions personnalisées.** `POST /v1/custom-functions` en crée une ; `GET /v1/custom-functions` liste celles que vous avez, et `GET`, `PUT` et `DELETE` sur `/v1/custom-functions/{customFunctionId}` permettent de lire, mettre à jour et supprimer une fonction. `POST /v1/custom-functions/test` effectue un test à blanc d'une définition avant que vous ne l'enregistriez.
2. **Créez et configurez l'agent.** `POST /v1/agents` le crée, `PUT /v1/agents/{agentId}` le met à jour, et `PATCH /v1/agents/{agentId}/active` avec `{ "active": false }` le maintient en pause pendant que vous travaillez (le même appel avec `true` le met en ligne). `GET /v1/agents` les liste.
3. **Donnez ses capacités à l'agent.** `POST /v1/agents/{agentId}/custom-functions` avec `{ "custom_function_id": "..." }` attache une fonction à l'agent ; le `DELETE /v1/agents/{agentId}/custom-functions/{customFunctionId}` correspondant la détache.
4. **Remplissez la bibliothèque multimédia.** `POST /v1/agents/{agentId}/media-library` téléverse un élément (JSON avec `base64Data`, `mimeType`, `title`, `description`) ; `GET` liste les éléments de l'agent, et `PATCH`/`DELETE` sur `/{itemId}` permettent de mettre à jour ou de supprimer un élément.
5. **Capturez-le en tant qu'instantané.**

```bash
curl -X POST "https://api.dmchamp.com/v1/snapshots" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Master setup v1",
    "agent_ids": ["AGENT_ID"],
    "include_knowledge": true,
    "include_tools": true,
    "include_media": true
  }'
```

À partir de là, il s'agit de la section précédente : définissez-le comme favori par défaut afin que chaque nouveau client en soit doté dès sa création, ou appliquez-le à la demande. La gestion courante se trouve à côté : `PATCH /v1/snapshots/{snapshotId}` avec `{ "name": "..." }` en renomme un, `DELETE /v1/snapshots/{snapshotId}` en supprime un (et retire le statut de favori s'il s'agissait du défaut), et `GET /v1/snapshots/apply-targets` liste tous les comptes sur lesquels vous pourriez effectuer une installation.

Les points de terminaison pour les agents, les fonctions personnalisées et les médias acceptent tous `sub_account_id`, de sorte que les mêmes appels peuvent également gérer un agent directement dans le compte d'un client. Les appels d'instantané agissent toujours sur votre compte d'agence — le modèle reste avec vous. Les schémas complets de requête et de réponse pour tous ces éléments se trouvent dans la [Référence de l'API](../api/reference.md).

***

## Gérer vos niveaux de tarification via l'API

`GET /v1/agency/pricing-tiers` · `POST /v1/agency/pricing-tiers` · `PATCH /v1/agency/pricing-tiers/{tierIndex}` · `DELETE /v1/agency/pricing-tiers/{tierIndex}`

Les plans que vous vendez dans **Mode SaaS → Niveaux de tarification** peuvent être lus et modifiés par code, afin que votre propre panneau d'administration ou script de provisionnement puisse ajouter un plan, ajuster un prix ou distribuer un lien de paiement sans que personne n'ait à ouvrir le tableau de bord. Authentifiez-vous avec votre clé API d'agence comme pour tout autre appel sur cette page ; ces points de terminaison sont au niveau de l'agence, ils ne prennent donc aucun `sub_account_id`. Chaque écriture exécute la même validation et la même synchronisation produit-et-prix Stripe que l'enregistrement dans le tableau de bord, de sorte qu'un plan créé ici est indiscernable de celui que vous avez configuré manuellement.

### Lister vos niveaux

```bash
curl "https://api.dmchamp.com/v1/agency/pricing-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "data": {
    "tiers": [
      {
        "tierIndex": 0,
        "credits": 1000,
        "price_cents": 2900,
        "currency": "usd",
        "label": "Starter",
        "billing_interval": "month",
        "trial_days": 14,
        "trial_credits": 250,
        "trial_card_required": false,
        "trial_hard_expiry": true,
        "stripe_price_id": "price_1PxAbC…",
        "stripe_product_id": "prod_QxAbC…",
        "checkout_url": "https://app.yourdomain.com/v1/checkout?id=YOUR_AGENCY_UID&tierIndex=0"
      }
    ],
    "count": 1,
    "max_tiers": 20
  }
}
```

Chaque niveau est renvoyé avec son **`tierIndex`** — sa position dans votre liste de Plans, qui est la manière dont les trois autres appels le désignent — et un **`checkout_url`** prêt à être partagé, le même lien que l'onglet **Paiements** vous donne, pointant déjà vers le [domaine en marque blanche](white-labeling.md) sur lequel ce plan est vendu.

### Ajouter un niveau

Le corps est un objet de niveau ; il est ajouté à la fin de votre liste.

```bash
curl -X POST "https://api.dmchamp.com/v1/agency/pricing-tiers" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Starter",
    "credits": 1000,
    "price_cents": 2900,
    "currency": "usd",
    "billing_interval": "month",
    "trial_days": 14,
    "trial_credits": 250,
    "trial_card_required": false,
    "trial_hard_expiry": true,
    "features": ["channels_3", "channel_whatsapp_web", "webhooks"]
  }'
```

La réponse contient le niveau créé, y compris le `tierIndex` sur lequel il a atterri et son `checkout_url`.

### Modifier un niveau

Envoyez uniquement les champs que vous souhaitez modifier ; tout le reste du plan est laissé tel quel.

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agency/pricing-tiers/0" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "price_cents": 3900, "trial_hard_expiry": true }'
```

Un champ que l'API ne reconnaît pas est refusé plutôt qu'ignoré, et l'erreur le nomme — ainsi, une faute de frappe ne peut jamais modifier silencieusement un paramètre qui semble actif mais ne fait rien. La modification du prix, des crédits, de la devise ou de la fréquence de facturation crée un nouveau prix dans votre Stripe ; les clients qui ont déjà souscrit restent sur ce pour quoi ils se sont inscrits.

### Supprimer un niveau

```bash
curl -X DELETE "https://api.dmchamp.com/v1/agency/pricing-tiers/2" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

Même règle que pour le tableau de bord : un plan qui a encore des abonnés actifs ne peut pas être supprimé. La demande est refusée, vous indiquant combien d'abonnés y sont inscrits — annulez ou migrez-les d'abord. Une suppression réussie répond avec vos niveaux restants, déjà renumérotés.

### Les champs d'un niveau

| Champ | Description |
|---|---|
| `label` / `description` | Le nom du plan et la ligne facultative affichée sur votre page de paiement. |
| `credits` | Crédits que le client obtient **par mois** sur un plan mensuel ou annuel, et **par période de facturation** sur un plan hebdomadaire. |
| `price_cents` | Prix par intervalle de facturation, dans la plus petite unité monétaire (`2900` = 29,00 $). Sur un plan annuel, il s'agit du prix de l'année entière. |
| `currency` | Code ISO en minuscules — `usd`, `eur`, `gbp`, etc. |
| `billing_interval` / `billing_interval_count` | `month` (par défaut), `year`, ou `week` avec un nombre de 1 à 52 pour « tous les N semaines ». |
| `trial_days` | Durée de l'essai gratuit, de 0 à 90. `0` (ou l'omettre) signifie aucun essai. |
| `trial_credits` | Crédits avec lesquels le client commence l'essai. Par défaut, le `credits` du plan. |
| `trial_card_required` | `false` permet au client de commencer l'essai sans saisir de carte. Par défaut `true`. |
| `trial_hard_expiry` | `true` renvoie les crédits d'essai inutilisés à votre réserve et verrouille le compte du client lorsqu'un essai se termine sans mise à niveau. Par défaut `false` — voir [Expiration stricte après l'essai](agency-accounts.md#step-3--set-up-pricing-tiers). |
| `rollover_cap_months` | Mois d'allocation que les clients de ce plan peuvent conserver entre deux renouvellements — un nombre de 0 à 120, fractions autorisées. `0` ne reporte rien ; `null` (par défaut) signifie aucun plafond. Voir [Plafonner ce qui est reporté](sub-accounts.md#capping-what-rolls-over). |
| `rollover_expiry_days` | Jours après lesquels les crédits inutilisés sont supprimés au renouvellement suivant — un nombre entier de 1 à 3650. `null` (par défaut) signifie qu'ils n'expirent jamais. |
| `features` / `feature_settings` | Ce que les clients de ce plan obtiennent — les mêmes identifiants de fonctionnalité que [Choisir les types de canaux qu'un client peut connecter](#choose-which-channel-types-a-client-can-connect). |
| `team_seats_limit` | Sièges d'équipe accordés par le plan : un nombre exact, `0` pour aucun, `-1` pour illimité. |
| `white_label_config` | Sur lequel de vos [domaines en marque blanche](white-labeling.md#up-to-three-white-labels) le plan est vendu. |

Les champs d'essai n'ont de sens que pour un plan qui inclut un essai : enregistrez un niveau avec `trial_days: 0` et ils seront supprimés. Les identifiants de produit et de prix Stripe du plan sont gérés pour vous et ne peuvent pas être définis manuellement.

Trois points à bien comprendre :

- **Les index de niveau sont des positions, pas des identifiants permanents.** La suppression d'un plan décale tous les plans suivants d'un rang vers le bas. Récupérez donc la liste après toute modification — et copiez à nouveau les liens de paiement que vous avez publiés, exactement comme vous le feriez après avoir supprimé un plan dans le tableau de bord.
- **Le mode SaaS doit être configuré en premier.** Ces points de terminaison nécessitent un compte d'agence avec marque blanche et une clé Stripe déjà enregistrée ; sans cela, il n'y a pas de compte Stripe sur lequel le produit et le prix du plan peuvent exister.
- **Vingt plans est la limite**, tout comme dans le tableau de bord. Le champ `max_tiers` dans la réponse de la liste vous indique la limite actuelle.

Les schémas complets de requête et de réponse se trouvent dans la [Référence de l'API](../api/reference.md), sous **Agence**.

***

## Définissez votre prix par crédit via l'API

`GET /v1/agency/credit-price` · `PATCH /v1/agency/credit-price`

Le prix que les clients paient pour les recharges ponctuelles (**Mode SaaS → Tarification par crédit**) peut également être lu et modifié par le code. Cette fonctionnalité est conçue pour les cas où le prix doit évoluer de manière autonome : une agence qui vend des crédits dans une devise mais facture dans une autre peut laisser une tâche planifiée réviser le prix en fonction des variations du taux de change, plutôt que de demander à quelqu'un de le modifier manuellement chaque semaine.

### Lire le prix actuel

```bash
curl "https://api.dmchamp.com/v1/agency/credit-price" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "data": {
    "price_per_credit_cents": 125,
    "price_per_credit_currency": "brl",
    "note": "USD 0.25 per credit at our reference rate",
    "minimum_cents": 60
  }
}
```

`minimum_cents` est le prix le plus bas autorisé par la plateforme dans cette devise, afin qu'une tâche puisse vérifier un nouveau prix avant de l'envoyer. Les trois valeurs sont `null` tant qu'aucun prix n'a été défini.

### Le modifier

```bash
curl -X PATCH "https://api.dmchamp.com/v1/agency/credit-price" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "price_per_credit_cents": 130, "currency": "brl" }'
```

Envoyez uniquement ce que vous souhaitez modifier. `price_per_credit_cents` est le prix dans la plus petite unité monétaire (`130` = 1,30 R$) ; `currency` est un code ISO en minuscules ; `note` est une ligne optionnelle de 200 caractères maximum affichée aux clients directement sous le prix par crédit sur leur page de facturation — pratique pour un prix de référence dans une autre devise, tel que *"0,25 USD par crédit à notre taux de référence"*. Envoyez `"note": ""` pour le supprimer. La réponse a la même forme que la lecture ci-dessus, ce qui permet à une tâche de comparer et d'ignorer l'écriture si rien n'a changé.

Les mêmes règles que dans le tableau de bord s'appliquent : le prix ne peut pas être inférieur au minimum de la plateforme pour cette devise, et le compte doit disposer de la marque blanche (white labeling). Contrairement aux points de terminaison des niveaux de tarification, aucune clé Stripe n'est requise pour lire ou modifier cette valeur.

### Donnez à une tâche une clé qui ne peut rien faire d'autre

Placer votre clé d'agence complète dans un planificateur donne plus d'accès qu'une mise à jour de prix ne le nécessite. À la place, créez une **clé à portée limitée** restreinte à la zone **Prix du crédit d'agence** : cette clé peut lire et modifier le prix par crédit et rien d'autre — elle ne peut pas toucher aux sous-comptes, aux plans, aux crédits ou à votre connexion Stripe.

```bash
curl -X POST "https://api.dmchamp.com/v1/api-keys" \
  -H "X-API-Key: YOUR_AGENCY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "label": "FX price updater", "scopes": { "read_only": false, "tags": ["Agency Credit Price"] } }'
```

La réponse contient la nouvelle clé dans `api_key` **une seule fois** — elle n'est plus jamais affichée, alors enregistrez-la immédiatement. Définissez `"read_only": true` pour une clé qui a seulement besoin de lire le prix, et ajoutez `"expires_at"` (une date ISO) si vous souhaitez qu'elle cesse de fonctionner d'elle-même. Seule la clé du propriétaire du compte peut créer des clés à portée limitée ; listez-les ou révoquez-les avec `GET /v1/api-keys` et `DELETE /v1/api-keys/{id}`.

***

## Laisser un sous-compte lire votre tarification

`GET /v1/subaccounts/agency-pricing`

Tous les autres points de terminaison sur cette page sont appelés avec **votre clé d'agence**, ciblant éventuellement un client via `sub_account_id`. Celui-ci est l'opposé : il est appelé avec la **propre clé API du sous-compte**, sans `sub_account_id`, afin que la propre page de recharge d'un client (ou une intégration que vous construisez pour lui) puisse afficher ce que vous lui facturez sans jamais voir votre compte d'agence.

```bash
curl "https://api.dmchamp.com/v1/subaccounts/agency-pricing" \
  -H "X-API-Key: THE_SUB_ACCOUNTS_OWN_API_KEY"
```

**Réponse**

```json
{
  "success": true,
  "data": {
    "tiers": [{ "credits": 1000, "price_cents": 2900, "currency": "usd" }],
    "price_per_credit_cents": 125,
    "price_per_credit_currency": "brl",
    "price_per_credit_note": "USD 0.25 per credit at our reference rate",
    "agency_display_name": "Client Co's Growth Partner"
  }
}
```

Ceci reflète exactement ce que [`GET /v1/agency/pricing-tiers`](#list-your-tiers) et [`GET /v1/agency/credit-price`](#read-the-current-price) vous renvoient en tant qu'agence, moins tout ce que le client n'a pas besoin de voir (identifiants Stripe, `max_tiers`, etc.). Cela ne fonctionne que pour un compte qui est réellement un sous-compte avec une agence liée — l'appeler depuis votre propre compte d'agence renvoie une erreur de permission.

***

## Points à garder à l'esprit

- **Utilisez votre clé d'agence.** Authentifiez chaque appel avec la clé API de votre compte d'agence — et non celle du sous-compte. Le paramètre `sub_account_id` est ce qui redirige l'action.
- **Les crédits proviennent du sous-compte.** Les achats et les frais récurrents sont débités du solde de crédit du sous-compte ciblé, et non du vôtre.
- **Une erreur `404` signifie « ce n'est pas votre sous-compte ».** Vérifiez l'identifiant et assurez-vous que le compte est bien l'un de ceux que vous gérez.
- **Le paramètre est facultatif partout où il est accepté.** Omettez-le et le même point de terminaison agira sur votre compte d'agence, vous permettant ainsi de réutiliser une seule intégration pour les deux.

***

## Connexe

- [Accès API](../integrations/api-access.md) — authentification, URL de base, erreurs, limites de débit.
- [Sous-comptes](sub-accounts.md) — lister et gérer les comptes que vous pouvez cibler.
- [Recharge automatique de sous-compte](sub-account-auto-recharge.md) — accorder des crédits à un sous-compte via webhook + API.
- [API Campagnes](../api/campaigns.md) — créer, mettre à jour et copier des campagnes, y compris la référence complète des champs.
- [API Connexion de canal](../api/channels.md) — connecter les canaux d'un client et les acheminer vers une campagne.
- Guide de l'API Analytics (dans la section API) — le cumul des sous-comptes d'agence et tous les autres points de terminaison de rapport.
- [Snapshots](snapshots.md) — ce qu'un instantané capture et comment en construire un dans le tableau de bord.
