
# API für Agenturen

Als Agentur können Sie dieselbe REST-API verwenden wie Ihre Kunden, jedoch einzelne Anfragen an eines Ihrer verwalteten **Unterkonten** anstatt an Ihr eigenes Konto richten. Dies ermöglicht es Ihnen, Tools zu entwickeln, mit denen Sie einen Kunden von Anfang bis Ende an Bord holen – von der Erstellung seiner Kampagnen über das Training seiner KI mit einer Wissensdatenbank, dem Importieren seiner Kontakte und dem Verbinden seiner Messaging-Kanäle bis hin zum Kauf von Telefonnummern –, ohne sich manuell bei jedem Unterkonto anmelden zu müssen.

Diese Seite behandelt nur das agenturspezifische Verhalten: wie man im Namen eines Unterkontos mit dem Parameter `sub_account_id` agiert. Für die Grundlagen (Generierung eines Schlüssels, Authentifizierung, Basis-URL, Fehlerformat, Ratenbegrenzungen) beginnen Sie mit dem Leitfaden zum [API-Zugriff](../integrations/api-access.md). Alles, was dort steht, gilt auch hier – Sie authentifizieren sich mit dem API-Schlüssel **Ihres Agenturkontos**.

::: note
**Hinweis:** Diese Seite ist technisch. Wenn Sie kein Entwickler sind, geben Sie sie an die Person weiter, die Ihre Integration erstellt.
:::


***

## Funktionsweise von „Im Namen von agieren“

Standardmäßig wirkt sich jede API-Anfrage auf das Konto aus, dem der API-Schlüssel gehört – Ihr Agenturkonto. Um stattdessen für ein verwaltetes Kundenkonto zu agieren, fügen Sie der Anfrage den optionalen Parameter `sub_account_id` hinzu und setzen Sie ihn auf die Konto-ID des Kunden.

- **`sub_account_id` weglassen** → die Anfrage wirkt sich auf Ihr eigenes Agenturkonto aus.
- **`sub_account_id` einfügen** → die Anfrage wirkt sich auf dieses Unterkonto aus, jedoch erst, nachdem die Plattform bestätigt hat, dass das Unterkonto tatsächlich Ihnen gehört.

Sie authentifizieren sich immer mit dem API-Schlüssel **Ihres Agenturkontos**. Sie benötigen niemals den eigenen Schlüssel des Unterkontos und müssen niemals die Anmeldedaten des Unterkontos verwalten.

### Wo der Parameter platziert wird

- **GET / DELETE-Endpunkte** → als Abfrageparameter übergeben: `?sub_account_id=THE_SUB_ACCOUNT_ID` (zusammen mit Ihrem `apiKey`, falls Sie sich per Abfrage authentifizieren).
- **POST / PUT / PATCH-Endpunkte** → im JSON-Anfragekörper als `"sub_account_id": "THE_SUB_ACCOUNT_ID"` einfügen.
- **KI-Assistenten** → nichts zu konfigurieren. Der [MCP-Server](../integrations/connect-ai-clients.md) übernimmt dieselbe Einstellung für seine Lese-Tools, sodass eine Verbindung mit Ihrem Agenturschlüssel über jeden Kunden berichten kann: Nennen Sie den Kunden einfach in Ihrer Anfrage („Wie viele Kontakte hat Bella's Bistro?“). Schreibaktionen sind ebenfalls verfügbar: Jeder Endpunkt, der `sub_account_id` akzeptiert, wird als Tool bereitgestellt, sodass Sie im Namen eines Kunden über dieselbe Verbindung erstellen, ändern und senden können.

### Die ID eines Unterkontos finden

Die `sub_account_id` ist die eindeutige ID des Kundenkontos. Sie erhalten die Liste Ihrer Unterkonten und deren IDs über die **SubAccounts**-API-Endpunkte (siehe den Leitfaden zu [Unterkonten](sub-accounts.md)) oder über die Seite **Unterkonten** in der Seitenleiste.

***

## Die Eigentümerschaft wird immer überprüft

Wenn Sie eine `sub_account_id` übergeben, prüft die Plattform, ob es sich um ein echtes Unterkonto handelt **und** ob es zu Ihrer Agentur gehört. Erst dann wird die Anfrage ausgeführt.

Wenn die ID unbekannt ist, kein Unterkonto darstellt oder zu einer anderen Agentur gehört, schlägt die Anfrage mit einer **`404`**-Antwort fehl:

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

> **Warum 404 und nicht 403?** Eine „Forbidden“-Antwort würde einem Außenstehenden verraten, dass die ID existiert, ihm aber nicht gehört. Die Rückgabe desselben `404` für „existiert nicht“ und „gehört dir nicht“ bedeutet, dass der Endpunkt nicht dazu verwendet werden kann, herauszufinden, welche Konto-IDs zu anderen Agenturen gehören. Betrachten Sie ein `404` hier als „Dies ist kein Unterkonto, das Sie verwalten“.

***

## Wo `sub_account_id` unterstützt wird

`sub_account_id` wird im Grunde bei jedem **Ressourcen**-Endpunkt akzeptiert – bei jedem Aufruf, der kontoeigene Daten erstellt, liest, aktualisiert oder löscht. In der Praxis können Sie die gesamte Einrichtung eines Unterkontos mit Ihrem Agenturschlüssel bereitstellen und ausführen:

- **KI-Einrichtung** – Kampagnen, Agenten, FAQs, Wissensdatenbank-Quellen (Website-Crawling **und** Dokumenten-Upload), Wissensdatenbank-Gruppen, Broadcasts, benutzerdefinierte Funktionen, MCP-Server
- **Kontakte & CRM** – Kontakte (einschließlich Import), Listen, Tags, Aufgaben, Deals, Termine, Ereignisse
- **Kanäle & Nummern** – WhatsApp / WhatsApp Web / Telegram / Instagram & Messenger / LINE verbinden, Telefonnummern suchen / kaufen / verwalten, WhatsApp-Vorlagen, Kanal-Routing
- **Messaging & Inhalte** – Nachrichten senden, Chat-Sitzungen, Chat-Exporte, tägliche Zusammenfassungen
- **Einstellungen & Integrationen** – Webhooks, Chat-Widget-Konfiguration, White-Label-Konfiguration, BYOK-SMS und andere Kontoeinstellungen, Analysen

Bei jedem dieser Punkte ist der Parameter **optional** — lassen Sie ihn weg, und der Aufruf erfolgt über Ihr Agenturkonto, sodass eine Integration für beides ausreicht. Guthaben und Nutzung werden immer von dem Konto abgebucht, auf das Sie abzielen: Gebühren für Kampagnen, Nachrichten, Tags und Nummern eines Unterkontos belasten das Guthaben **des Unterkontos**.

### Wo dies NICHT gilt

Einige Endpunkte sind auf Agenturebene angesiedelt oder beziehen sich auf das eigene Konto und ignorieren `sub_account_id`:

- **Verwaltung der Unterkonten selbst** — die SubAccounts-Endpunkte (Erstellen / Auflisten / Aktualisieren eines Unterkontos) und der BYOK-Ausgabenlimit-Endpunkt benennen das Unterkonto bereits in ihrem eigenen URL-Pfad. Die [Preis- und Richtlinien-Endpunkte](#set-per-client-ai-pricing-and-policy) und [Chat-Überwachungs-Endpunkte](#read-a-sub-accounts-conversations) folgen demselben Muster.
- **Kopieren eines Agenten zwischen Konten** — `POST /v1/subaccounts/agents/copy` benennt beide Konten selbst und verwendet das Ziel als `targetUserId`. Siehe das [praktische Beispiel](#worked-example-ship-a-template-agent-into-every-new-client) unten. (Das ältere `POST /v1/subaccounts/campaigns/copy` funktioniert auf die gleiche Weise, ist aber zusammen mit dem Rest der [Campaigns API](../api/campaigns.md) veraltet.)
- **Anpassen von Credits und die zwei agenturweiten Zusammenfassungen** — [`POST /v1/subaccounts/credits`](#grant-or-deduct-credits-directly) identifiziert das Unterkonto stattdessen über `email`; [`GET /v1/subaccounts/credit-usage`](#read-credit-usage-and-campaign-health-across-your-book) und `GET /v1/subaccounts/campaign-status` berichten über alle Unterkonten gleichzeitig, daher gibt es kein einzelnes Konto, das angesprochen werden kann.
- **Das eigene Konto Ihrer Agentur** — API-Schlüsselverwaltung, Berichte zur Agenturnutzung, Teamverwaltung und Ihre [Preisstufen](#manage-your-pricing-tiers-over-the-api) beziehen sich immer auf Ihr Agenturkonto.
- **Webhooks für eingehende Nachrichten** — Endpunkte, an die externe Systeme Daten *senden*, sind an das Konto gebunden, dessen Anmeldedaten sie konfiguriert haben, daher gibt es nichts umzuleiten.

> Die stets aktuelle, maschinenlesbare Liste der Parameter, die jeder Endpunkt akzeptiert, finden Sie in der API-Referenz Ihres Dashboards (**Einstellungen → Integrationen → API-Schlüssel**) sowie in der OpenAPI-Spezifikation unter `GET /v1/docs/openapi.yaml`. Wir veröffentlichen häufig API-Änderungen – betrachten Sie diese als die maßgebliche Informationsquelle.

::: master-only
<figure><img src="../.gitbook/assets/v2-api-access-key-section.png" alt="API-Schlüsseleinstellungsseite mit maskiertem Schlüssel und Regenerieren-Steuerelement"><figcaption><p>Einstellungen → Integrationen → API-Schlüssel — hier befindet sich der Schlüssel Ihrer Agentur, zusammen mit dem Link zur vollständigen API-Referenz.</p></figcaption></figure>
:::

***

## Praxisbeispiel: Instagram & Messenger für ein Unterkonto verbinden

Das Verbinden von Instagram & Messenger ist ein browserbasierter Ablauf. Sie starten ihn über die API, übergeben dem Kunden die zurückgegebene Einwilligungs-URL (oder öffnen sie für ihn), warten darauf, dass er die Autorisierung in seinem Browser vornimmt, und wählen dann die zu verbindende Seite aus – all dies, während Sie mit `sub_account_id` auf sein Unterkonto zielen.

### Schritt 1 – Die Verbindung starten

Rufen Sie den Connect-Endpunkt mit dem `sub_account_id` des Kunden im Body auf. Hier werden keine Anmeldedaten gesendet; die Plattform gibt eine Einwilligungs-URL zurück, die der Kunde in einem Browser öffnen muss, sowie ein einmaliges Korrelationstoken.

**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
```

**Antwort:**

```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"
}
```

Leiten Sie den Kunden zur Autorisierung in einem Browser an `oauth_url` weiter. Das `state_token` korreliert diesen Versuch und ist ein kurzlebiges Geheimnis – protokollieren Sie es nicht. Der Versuch läuft bei `expires_at` ab; falls er abläuft, starten Sie erneut.

### Schritt 2 – Abfragen, bis die Seiten geladen sind

Nachdem der Client autorisiert hat, fragen Sie den Status-Endpunkt ab (mit demselben `sub_account_id`, diesmal als Abfrageparameter), bis die verbindbaren Seiten erscheinen.

**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"]
```

**Antwort:**

```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
}
```

Das Feld `status` durchläuft `pending` → `token_received` → `pages_loaded` → `connected`. Warten Sie auf `pages_loaded`, bevor Sie eine Seite auswählen. Anstatt fortzufahren, können auch zwei terminale Fehlerzustände auftreten: `failed` und `expired` (der Client hat die Zustimmung verweigert oder das ca. 30-minütige Zeitfenster des State-Tokens ist abgelaufen) — ein `reason`-Feld ist enthalten, wenn einer dieser Fehler auftritt. Beenden Sie das Polling und starten Sie bei Schritt 1 neu, falls einer dieser Fehler angezeigt wird; warten Sie nicht ewig auf `pending`. Seiten-Zugriffstoken werden niemals zurückgegeben.

### Schritt 3 — Wählen Sie die zu verbindende Seite aus

Wählen Sie eine der Seiten-IDs aus Schritt 2 aus. Durch die Auswahl einer Seite werden sowohl Instagram als auch Messenger für diese Seite verbunden. Fügen Sie `sub_account_id` erneut in den Body ein.

**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()
```

**Antwort:**

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

Das war's – Instagram und Messenger sind jetzt mit dem Unterkonto des Clients verbunden. Sie haben lediglich das `page_id` bereitgestellt; die zugrunde liegende Berechtigung wird auf dem Server aufgelöst und niemals an Ihre Integration weitergegeben.

***

## Praxisbeispiel: Eine Nummer für ein Unterkonto kaufen

Der Kauf einer Nummer funktioniert auf die gleiche Weise: Suchen Sie mit `sub_account_id` in der Abfrage und kaufen Sie sie dann mit dem Wert im Body. Die Credits werden vom Guthaben **des Unterkontos** abgebucht und die Nummer wird für das Unterkonto bereitgestellt.

**Suche (cURL):**

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

**Kauf (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();
```

**Kauf (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()
```

**Antwort:**

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

Die Nummer wird im Status `PURCHASED` bereitgestellt und die Registrierung des WhatsApp-Absenders läuft im Hintergrund weiter. Pollen Sie `GET /v1/phone-numbers/{phoneNumber}/status?sub_account_id=abc123def456`, bis der Status `ONLINE` erreicht, bevor Sie Nachrichten senden.

***

## Praktisches Beispiel: Einen Vorlagen-Agenten für jeden neuen Kunden bereitstellen

Das übliche Agenturmodell besteht darin, einen Master-Agenten in Ihrem Agenturkonto zu führen, der genau so eingestellt ist, wie jeder Kunde starten soll, und bei der Bereitstellung eine Kopie davon in jedes neue Unterkonto zu übertragen. Das sind drei Aufrufe, und danach muss nichts mehr wiederholt werden: Die Kopie behält ihre Einstellungen bei, bis Sie sie ändern.

### Schritt 1 — Den Agenten kopieren

`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
  }'
```

Die Antwort enthält die ID des neuen Agenten unter `data.agent_id`. Die FAQs, die Wissensdatenbank und die Medienbibliothek werden mit kopiert; die WhatsApp-Vorlagen, verknüpften Social-Media-Beiträge und Kontakte des Quellkontos werden bewusst nicht übernommen. Die vollständige Feldliste finden Sie in der [AI Agents API](../api/agents.md#copy-an-agent-into-a-sub-account-agencies).

Beachten Sie, dass dieser Endpunkt `targetUserId` anstelle von `sub_account_id` verwendet — er benennt beide Konten selbst. Die beiden folgenden Aufrufe verwenden den normalen `sub_account_id`-Parameter.

### Schritt 2 — Einschalten

Die Kopie kommt immer pausiert an, sodass sie niemanden benachrichtigen kann, bis Sie dies zulassen. Dies ist auch der richtige Zeitpunkt, um die KI-Stufe festzulegen, auf der sich der Kunde befinden soll; sie bleibt dort, sodass sie nicht nach einem Zeitplan erneut angewendet werden muss.

```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" }'
```

Um zu verhindern, dass der Kunde die Stufe nachträglich ändert, [sperren Sie die zulässigen Stufen](#set-per-client-ai-pricing-and-policy) für das Unterkonto, anstatt den Wert erneut zu senden.

### Schritt 3 — Die Kanäle des Kunden darauf ausrichten

Die Kopie kommt auch ohne Routing an, sodass sie erst reagiert, wenn Sie sie als Antwortinstanz für die Kanäle festlegen, die der Kunde verbunden hat. Ein Aufruf pro Kanal:

```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" }'
```

Von hier an wird eine erste Nachricht von einem unbekannten Kontakt auf diesem Kanal automatisch vom kopierten Agenten empfangen. Siehe [Einen Kanal auf einen Agenten ausrichten](../api/entry-points.md#point-a-channel-at-an-agent) für die anderen Kanäle und das Routing pro Nummer.

> **Legen Sie die Zeitzone des Kunden fest, wenn Sie das Unterkonto erstellen.** Übergeben Sie `time_zone_id` bei `POST /v1/subaccounts`. Die aktiven Stunden der Kampagne werden in der eigenen Zeitzone des Unterkontos ausgewertet. Ein Kunde, der ohne Zeitzone erstellt wurde, wird daher nach UTC abgerechnet – was sich unbemerkt darauf auswirkt, wann der Assistent antworten darf.

***

## Überspringen des Einrichtungsassistenten für einen Kunden, den Sie selbst konfigurieren

`POST /v1/subaccounts`

Standardmäßig wird der Besitzer eines neuen Unterkontos bei der ersten Anmeldung durch den geführten Einrichtungsassistenten geleitet. Für Kunden, bei denen Sie die Arbeit übernehmen – also die Kampagne erstellen und die Kanäle verbinden, bevor sich der Kunde überhaupt anmeldet – übergeben Sie `guided_onboarding: false`, wenn Sie das Konto erstellen. Sie landen stattdessen auf dem Dashboard und der Eintrag **Einrichtungsassistent** wird in ihrer Seitenleiste ausgeblendet.

```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 }
  }'
```

Lassen Sie das Feld weg (oder senden Sie `true`) und der Assistent verhält sich genau wie immer, sodass bestehende Integrationen keine Änderungen benötigen. Um einem Kunden den Assistenten später wieder zur Verfügung zu stellen, zeigen Sie das Element `guided_onboarding` wieder mit `PUT /v1/subaccounts/{subAccountUid}/menu-visibility` (unten) an — die Menüsichtbarkeit steuert, ob der Assistent erreichbar ist, `guided_onboarding` steuert nur die Weiterleitung bei der ersten Anmeldung.

***

## Aufgaben, tägliche Zusammenfassungen oder die Medienbibliothek für einen Kunden deaktivieren

`POST /v1/subaccounts`

Diese drei Funktionen sind für jeden neuen Kunden aktiviert, sofern Sie nichts anderes angeben, und sie verhalten sich anders als alle anderen Funktionen in diesem Leitfaden: Sie sind **Opt-out**-Funktionen, keine Opt-in-Funktionen. Sie einfach aus `features` wegzulassen, reicht nicht aus, da die `features`-Liste einer älteren Integration sie schlichtweg nie erwähnte – wir können nicht unterscheiden, ob "die Agentur dies deaktiviert hat" oder "diese Liste geschrieben wurde, bevor die Option existierte".

Geben Sie es daher explizit mit `feature_settings` an:

```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
    }
  }'
```

Jeder Schlüssel ist optional; alles, was Sie weglassen, bleibt aktiviert. Mit `tasks: false` hört die KI auf, Aufgaben für diesen Kunden zu erstellen, und es werden keine "Neue Aufgabe erstellt"-E-Mails versendet; mit `daily_summaries: false` wird die nächtliche Zusammenfassung weder generiert noch per E-Mail verschickt.

`feature_settings` ist das Einzige, was diese drei Funktionen bei der Erstellung deaktiviert. Sie aus `features` wegzulassen, bewirkt für sich genommen nichts, egal wie der Rest Ihrer Liste aussieht – das ist beabsichtigt, damit eine ältere Integration nicht stillschweigend alle drei verliert.

Um dies nachträglich zu ändern, senden Sie die vollständige `features`-Liste an `PUT /v1/subaccounts/{subAccountUid}/features` – dort schaltet das Vorhandensein in der Liste eine Funktion ein und das Fehlen schaltet sie aus.

***

## Automatische Anmeldung Ihrer Kunden in deren Unterkonto (SSO)

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

Ein einziger Aufruf mit Ihrem Agentur-API-Schlüssel liefert eine sofort einsatzbereite URL, die den Kunden direkt in seinem eigenen Unterkonto anmeldet – ohne Anmeldebildschirm, ohne Passwortabfrage, ohne dass Sie etwas zusätzlich entwickeln müssen. Öffnen Sie diese in einem neuen Tab, per Weiterleitung oder in einem iframe innerhalb Ihres eigenen Produkts.

| Feld | Erforderlich | Beschreibung |
|---|---|---|
| `redirect` | Nein | In-App-Seite, auf der der Client landen soll, z. B. `"/chats"` oder `"/agents"`. Wird als `deep_link_url` in der Antwort zurückgegeben. |
| `app_base_url` | Nein | Dashboard-Host für den Link. Standardmäßig Ihre White-Label-App-Domain (oder die Plattform-Domain, falls Sie keine haben). Muss `https` sein. |

**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" }'
```

**Antwort**

```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"
}
```

So nutzen Sie es optimal:

- **Ein Sprung.** Das Öffnen von `url` meldet den Kunden an und führt ihn direkt auf die `redirect`-Seite des Dashboards – ohne Anmeldebildschirm oder Zwischenseite. `deep_link_url` benennt dasselbe Ziel für Integratoren, die es bevorzugen, nach der Anmeldung explizit zu einem Frame zu navigieren; sobald die Sitzung besteht, funktioniert jeder Dashboard-Pfad in diesem Browser-Kontext.
- **Bei Bedarf erstellen, sofort öffnen.** Der Link enthält ein Anmelde-Credential und läuft nach etwa einer Stunde ab. Fordern Sie ihn serverseitig in dem Moment an, in dem der Kunde klickt, und speichern oder versenden Sie ihn niemals per E-Mail.
- Das Anmelde-Token wird im URL-Fragment (`#…`) übertragen, das Browser niemals an Server senden, und es wird aus der Adressleiste entfernt, sobald es verbraucht wurde.
- **Nur Ihre eigenen Unterkonten.** Der Endpunkt lehnt jedes Konto ab, das nicht Ihrer Agentur gehört.
- Ein abgelaufener Link zeigt einen klaren Fehler mit einem Pfad für einen erneuten Versuch an – erstellen Sie einfach einen neuen.

***

## Navigationselemente in einem Unterkonto ausblenden

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

Steuert, welche Seitenleisten- und Einstellungsoptionen ein Unterkonto sieht – nützlich, wenn Sie das Dashboard einbetten und nur die Bereiche anzeigen möchten, die Ihr Produkt noch nicht abdeckt. Alles, was nicht aufgeführt ist, bleibt sichtbar; senden Sie `null` als gesamten `menuVisibility`-Wert, um alles wieder auf sichtbar zurückzusetzen. Das Ausblenden eines Elements verbirgt den Menüeintrag – kombinieren Sie dies mit den Funktionen, die Sie dem Unterkonto gewähren, um den Zugriff strikt zu regeln.

**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 }
    }
  }'
```

**Antwort**

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

`side_nav` akzeptiert diese 13 Schlüssel, die den Namen der Seitenleistenelemente entsprechen: `Dashboard`, `DailySummaries`, `Chats`, `Contacts`, `Deals`, `Tasks`, `Automations`, `Campaigns`, `Appointments`, `Settings`, `Help`, `CreditsCounter` (das in der Seitenleiste angezeigte Guthaben) und `guided_onboarding` (der Einrichtungsassistent). Drei weitere Schlüssel — `AiInsights`, `Sub Accounts` und `Agency Reselling` — werden zwar akzeptiert, bewirken jedoch nichts: Sie galten nur für das eingestellte klassische Dashboard, daher hat ihre Festlegung keine Auswirkungen auf Ihre Unterkonten. Fehlende Schlüssel bedeuten sichtbar; wenn Sie sich selbst beim Unterkonto anmelden, werden ausgeblendete Elemente vorübergehend angezeigt, sodass Sie die Einstellungen jederzeit wieder ändern können.

Das Ausblenden einer Seite aus dem Menü gewährt niemals Zugriff darauf. `Automations` erfordert, dass die Funktion `automations` für das Unterkonto gewährt wurde – setzen Sie den Schlüssel auf `true`, ohne dass dies der Fall ist, wird die Seite dennoch nicht angezeigt. `Tasks` und `DailySummaries` funktionieren genau umgekehrt: Sie sind für jeden Kunden aktiviert, es sei denn, Sie schalten sie aus (siehe [Aufgaben, tägliche Zusammenfassungen oder die Medienbibliothek für einen Kunden deaktivieren](#turn-tasks-daily-summaries-or-the-media-library-off-for-a-client)).

***

## Wählen Sie aus, welche Kanaltypen ein Client verbinden kann

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

Die **Kanaltypen**-Schalter, die Sie auf einer Planebene sehen, sind gewöhnliche Feature-IDs. Sie können diese also pro Client über die API anstatt über das Dashboard festlegen. Dies ist einer der Endpunkte, der das Unterkonto in seiner eigenen URL benennt, daher benötigt er kein `sub_account_id`.

| Funktions-ID | Kanal |
|---|---|
| `channel_chat_widget` | Website-Chat-Widget |
| `channel_whatsapp_api` | WhatsApp Business API |
| `channel_whatsapp_web` | WhatsApp Web (QR-verknüpfte Nummer) |
| `channel_instagram` | Instagram |
| `channel_messenger` | Facebook Messenger |
| `channel_telegram` | Telegram |
| `channel_line` | LINE |
| `channel_viber` | Viber |
| `channel_email` | E-Mail-Postfach |
| `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"
    ]
  }'
```

Drei Dinge, die Sie beachten sollten:

- **Der Aufruf ersetzt die gesamte Feature-Liste.** Senden Sie jedes Feature, das der Client behalten soll, nicht nur die, die Sie ändern. Die gleichen IDs funktionieren als `features` unter `POST /v1/subaccounts`, wenn Sie das Konto erstellen.
- **Kanaltypen und Kanalanzahl sind separate Schranken, und beide gelten.** `channels_1` / `channels_3` / `channels_unlimited` steuern *wie viele* Verbindungen; die `channel_*`-IDs steuern *welche Typen*. Das obige Beispiel bedeutet "bis zu 3 Verbindungen, und nur Chat-Widget, WhatsApp Web oder Instagram".
- **Das Senden von gar keinen `channel_*`-IDs bedeutet keine Kanaleinschränkung.** Das ist das ursprüngliche Verhalten, weshalb bestehende Clients nicht betroffen waren, als dies eingeführt wurde. Senden Sie eine oder mehrere, und alles andere wird auf der Kanalseite des Clients als gesperrt mit einem Upgrade-Hinweis anstelle einer Verbinden-Schaltfläche angezeigt. Kanäle, die der Client bereits verbunden hat, funktionieren weiterhin.

> Das Festlegen der Kanalliste auf einer **Planebene**, sodass jeder Client, der diese Ebene kauft, sie erbt, erfolgt im Dashboard unter Ihren Agentur-Planeinstellungen. Dieser Endpunkt legt sie für ein spezifisches Unterkonto fest.

***

## Legen Sie ein exaktes Limit für Teammitglieder eines Kunden fest

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

Die `team_seats_*`-Funktionen bieten nur voreingestellte Stufen (3 / 5 / 10 / unbegrenzt). Um einem Kunden eine **exakte** Anzahl an Team-Plätzen zuzuweisen – 2, 7, 15 oder eine beliebige andere Zahl –, legen Sie stattdessen `usage_limits.team_seats_limit` fest. Diese Einstellung hat Vorrang vor den Voreinstellungen und wird von der Plattform bei jeder Einladung, jedem direkten Hinzufügen und jeder Einladungsannahme durchgesetzt: Sobald das Limit erreicht ist, werden weitere Einladungen serverseitig abgelehnt.

- Eine positive Ganzzahl ist das exakte Limit.
- `0` bedeutet, dass Teammitglieder **nicht enthalten** sind – der Kunde kann niemanden einladen.
- `-1` bedeutet unbegrenzt.
- `null` löscht das benutzerdefinierte Limit und greift auf die `team_seats_*`-Voreinstellung zurück, die in der Funktionsliste festgelegt ist.

Das Senken des Limits entfernt niemals bestehende Teammitglieder; es verhindert lediglich das Hinzufügen neuer Mitglieder.

```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 }
  }'
```

Sie können es auch zum Zeitpunkt der Erstellung festlegen: `POST /v1/subaccounts` akzeptiert `usage_limits.team_seats_limit` mit derselben Semantik. Um den aktuellen Wert zu lesen, rufen Sie das Unterkonto mit `GET /v1/subaccounts?email=...` ab und sehen Sie sich `usage_limits.team_seats_limit` an (nicht vorhanden/`null` = die Voreinstellungen entscheiden). Derselbe Endpunkt aktualisiert auch `credits`, `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months`, `rollover_expiry_days` und `byok_monthly_limit_usd` – senden Sie nur die Schlüssel, die Sie ändern möchten.

**Wie sich dies auf die Sitzplatzbeschränkungen von SaaS-Plänen auswirkt.** Ihre SaaS-Pläne können über ein eigenes Sitzplatzkontingent verfügen (festgelegt im Plan-Editor – siehe [Team-Sitzplätze in einem Plan](agency-accounts.md#step-3--set-up-pricing-tiers)), das automatisch angewendet wird, wenn ein Kunde ein Abonnement abschließt. Ein Limit, das Sie über diesen Endpunkt festlegen, zählt als **manuelle** Zuweisung: Der Kauf eines Plans ersetzt diese durch das eigene Sitzplatzkontingent des Plans (da dieser Kauf eine explizite Entscheidung für einen Plan darstellt), aber unbeaufsichtigte monatliche **Verlängerungen überschreiben niemals ein manuelles Limit** – eine einmalige Ausnahme, die Sie einem Kunden gewähren, bleibt also über dessen Abrechnungszyklus hinweg bestehen. Das Löschen des manuellen Limits mit `null` übergibt die Kontrolle bei der nächsten Verlängerung wieder an den Plan.

**Begrenzen Sie, was ein Kunde zwischen Verlängerungen mitnimmt.** Zwei weitere `usage_limits`-Schlüssel befinden sich neben `roll_over_to_next_month`. Beide werden auch von `POST /v1/subaccounts` zum Zeitpunkt der Erstellung akzeptiert, und `null` löscht beide.

| Schlüssel | Funktion |
|---|---|
| `rollover_cap_months` | Monate des Guthabens, die der Kunde behalten darf. Eine Zahl von 0 bis 120, Brüche sind erlaubt (`0.5` = ein halber Monat). Bei jeder Verlängerung wird das ungenutzte Guthaben auf maximal das Vielfache des Guthabens gekürzt, das diese Verlängerung gewährt, bevor die neuen Credits hinzugefügt werden; `0` überträgt nichts. |
| `rollover_expiry_days` | Eine ganze Anzahl von Tagen, 1 bis 3650. Credits, die so lange ungenutzt bleiben, verfallen bei der ersten Verlängerung, nachdem sie dieses Alter erreicht haben. Der Verbrauch wird immer von den ältesten Credits abgezogen, sodass ein Kunde, der sein Guthaben jeden Monat aufbraucht, nie etwas verliert. |

Wenn sie nicht festgelegt sind, greifen beide auf den Plan des Kunden zurück; ein hier gesendeter Wert hat Vorrang vor dem des Plans. Nur wiederkehrende Credits (das monatliche Guthaben und Plan-Credits) unterliegen diesen: Aufladungen, automatische Aufladungen und einmalige Hinzufügungen werden niemals begrenzt oder laufen ab. Jede Kürzung wird als **Rollover-Kürzung-Guthabenanpassung** oder **Abgelaufene-Credits-Guthabenanpassung** in den Guthabenverlauf des Kunden geschrieben und zählt niemals als Verbrauch. Die Entsprechungen auf Planebene sind `rollover_cap_months` und `rollover_expiry_days` in einer Preisstufe – siehe [Die Felder einer Stufe](#the-fields-on-a-tier) und [Begrenzung der Übertragung](sub-accounts.md#capping-what-rolls-over).

***

## KI-Preise und -Richtlinien pro Kunde festlegen

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

Acht weitere Schalter pro Kunde, zusätzlich zu `/limits`, `/features` und `/menu-visibility` oben. Jeder verwendet die UID des Unterkontos in der URL (kein `sub_account_id` Body-/Query-Parameter – das Ziel ist bereits im Pfad benannt) und ist auf die gleiche Weise begrenzt: Ihr Agenturschlüssel, und das Unterkonto muss zu Ihrer Agentur gehören.

**Welche KI-Modelle ein Kunde verwenden kann**

```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 }` schaltet den Kunden für die Max-KI-Stufe frei (oder ab) — unsere Infrastruktur zum Listenpreis der Plattform. Dies für einen BYOK-Kunden zu aktivieren, ändert dessen KI-Kosten von „kostenlos mit eigenem Schlüssel“ zu „wird von meinem Guthabenpool abgebucht“, es ist also eine bewusste Entscheidung pro Kunde und keine agenturweite Standardeinstellung.

Um einzuschränken, AUS WELCHEN Stufen die Kampagnen und Agenten eines Kunden überhaupt wählen dürfen (anstatt nur Max zu sperren), verwenden Sie `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` ist ein Array, das aus `standard`, `economy`, `max`, `mini` stammt — es ERSETZT die Zulassungsliste des Kunden. Senden Sie `null` (oder `[]`), um die Einschränkung aufzuheben und ihnen die Wahl jeder Stufe zu ermöglichen. Dies ist wichtig, da ein Unterkonto, das seine eigene KI-Stufe wählt, von **Ihrem** Guthabenpool zehrt. Es ist also der Hebel, um festzulegen, welche Modelle ein Reseller-Kunde auf Ihre Kosten nutzen darf.

**Eine Antwortnachricht, während der Kunde kein Guthaben mehr hat**

```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." }'
```

Wenn das Guthaben des Kunden (oder Ihr Pool) leer ist, kann die KI nicht antworten und der Kontakt hört nichts. Mit `enabled: true` erhält jeder Kontakt, der während des Ausfalls schreibt, einmalig `message` (max. 500 Zeichen, wird auf jedem Kanal unverändert gesendet), und die KI beantwortet diese Konversationen tatsächlich, sobald wieder Guthaben vorhanden ist. `enabled: false` speichert den Text für später; `enabled: false` ohne `message` entfernt die Einstellung. Derselbe Schalter wie **Antwortnachricht bei fehlendem Guthaben** im Bearbeitungs-Modal des Unterkontos – siehe [Eine Antwortnachricht, während ein Kunde kein Guthaben mehr hat](sub-accounts.md#a-holding-reply-while-a-client-is-out-of-credits).

**Was ein Kunde pro KI-Aktion zahlt und der WhatsApp-Gebührenaufschlag**

Zwei Möglichkeiten, Ihren kundenseitigen Tarif festzulegen, von einfach bis detailliert:

```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` ist der Preis in Credits, den das EIGENE Guthaben des Unterkontos pro Max-Modell-KI-Aktion verbraucht — Ihr kundenseitiger Aufschlag auf das, was Ihr Pool tatsächlich zahlt. `null` setzt die Überschreibung auf den Listenpreis der Plattform zurück. Der Satz muss mindestens so hoch sein wie die Kosten einer Max-Aktion für Ihren eigenen Pool (damit Sie einen Kunden niemals unter Ihren Kosten bepreisen können) und darf maximal 10 Credits betragen; eine Anfrage außerhalb dieses Fensters wird mit dem berechneten Mindestwert in der Fehlermeldung abgelehnt.

Für eine Preisgestaltung pro Aktionstyp anstelle eines pauschalen Max-Satzes verwenden Sie `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` ist eine ZUSAMMENFÜHRUNG mit der bestehenden Zuordnung des Kunden — ein Schlüssel, den Sie nicht erwähnen, bleibt wie er war, und `null` setzt diesen Schlüssel auf seinen Standardwert zurück. Die erkannten Schlüssel:

| Schlüssel | Preise |
|---|---|
| `AI_MESSAGE` | Eine KI-Antwort |
| `AI_TOOL_USE` | Ein KI-Tool-Aufruf |
| `EVALUATION_CALL` | Ein Chat-Bewertungsdurchlauf |
| `INTERRUPTION_HANDLING` | Behandlung einer Unterbrechung während der Antwort |
| `CONTACT_TAG` | Ein KI-zugewiesenes Kontakt-Tag |
| `CHAT_SUMMARY` | Eine Chat-Zusammenfassung |
| `wa_carrier_multiplier` | Ein Aufschlag-Multiplikator, der auf jede Nicht-KI-WhatsApp-Gebühr angewendet wird, die der Kunde zahlt: monatliche Nummernmiete, Zustellungsgebühren für verwaltete Leitungen und Meta/Twilio-Template-Durchlaufkosten. |

Die Raten pro Aktion müssen eine Zahl größer als 0 und bis zu 10 sein; `wa_carrier_multiplier` muss mindestens `1` betragen (kein Rabatt unter den Kosten) und darf maximal 10 sein. Das Senden eines nicht erkannten Schlüssels oder eines Wertes außerhalb des Bereichs führt zur Ablehnung der GESAMTEN Anfrage und benennt jeden fehlerhaften Schlüssel, sodass ein Tippfehler niemals stillschweigend einen Preis speichern kann, der eigentlich nicht angewendet wird.

Wenn Sie Mitglied im [Champions Circle](https://skool.com/dm-champions) sind, überträgt `insider-rate` Ihren 20%-Rabatt für Max/Lead Finder auf einen einzelnen Kunden, anstatt ihn agenturweit anzuwenden:

```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 }'
```

Das Aktivieren erfordert, dass Ihr eigenes Agenturkonto tatsächlich eine Circle-Mitgliedschaft besitzt; das Deaktivieren ist jederzeit möglich, sodass ein Mitglied, dessen Status abgelaufen ist, die Einstellungen für einen Kunden jederzeit zurücksetzen kann.

**Abschnitte des Playbooks eines Kunden sperren**

```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` ist ein Array, das aus `instructions`, `goal`, `rules`, `personality`, `conclude_unless` stammt — es ERSETZT die gesperrte Liste des Kunden. Ein gesperrter Abschnitt wird serverseitig abgelehnt, wenn das UNTERKONTO selbst versucht, ihn zu ändern (direkt oder per API-Schlüssel), während Sie (über `sub_account_id`) und die Dashboard-Admin-Ansicht des Kunden weiterhin alles bearbeiten können. Senden Sie `null` (oder `[]`), um alles zu entsperren. Nützlich für „Done-for-you“-Kunden, bei denen Sie das Playbook besitzen und nach dem Ergebnis beurteilt werden.

**Benachrichtigungseinstellungen eines Kunden in dessen Namen festlegen**

```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` ersetzt das gesamte Benachrichtigungs-Einstellungsset des Kunden (keine Zusammenführung pro Schlüssel – senden Sie jede Kategorie, die beibehalten werden soll, entsprechend der Speicherung auf der Einstellungsseite des Unterkontos). Jede Kategorie unter `settings` akzeptiert `enabled` (boolean) und bis zu drei `channels` aus `email`, `in_app`, `webhook`. Senden Sie `null`, um die Plattform-Standardeinstellungen wiederherzustellen.

Alle sieben Endpunkte antworten mit `{ "success": true, "data": { "subAccountUid": "...", ...the field(s) you set... } }` und werden mit dem Vorher-/Nachher-Wert protokolliert. Häufige Fehler: `403`, wenn Ihr Konto kein Agentur-/Entwicklerkonto ist oder das Unterkonto nicht von Ihnen verwaltet wird, `400`, wenn es sich nicht um ein Agentur-Unterkonto handelt oder ein Wert außerhalb des zulässigen Bereichs liegt.

***

## Einen Kunden pausieren, der sein Abonnement ausgesetzt hat

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

Wenn ein Kunde sein Abonnement bei Ihnen aussetzt, pausieren Sie dessen Konto, anstatt es zu löschen: Alles, was der Kunde sendet, stoppt sofort — ausgehende Nachrichten, Broadcasts, KI-Antworten auf allen Kanälen — und wenn sich der Kunde anmeldet, sieht er anstelle der App eine bildschirmfüllende **Konto pausiert**-Sperre (mit Ihrer optionalen Nachricht). Nichts wird gelöscht oder getrennt: Agenten, Kampagnen, verbundene Kanäle, Kontakte und der Chatverlauf bleiben exakt so, wie sie sind. Das Aufheben der Pause bringt den Kunden also genau dorthin zurück, wo er aufgehört hat — ohne dass eine erneute Einrichtung erforderlich ist.

| Feld | Erforderlich | Beschreibung |
|---|---|---|
| `message` | Nein | Wird dem Kunden auf seinem Sperrbildschirm angezeigt. Lassen Sie es weg für den Standardtext. |
| `reason` | Nein | Agenturinterne Notiz, die mit der Pause gespeichert wird und im Audit-Log erscheint — wird dem Kunden niemals angezeigt. |

**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" }'
```

**Antwort**

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

Wenn der Kunde zurückkehrt, hebt `POST /v1/subaccounts/SUB_ACCOUNT_UID/unpause` (kein Body) die Sperre auf — das Senden und die KI-Antworten werden sofort wieder aufgenommen.

Wissenswertes:

- **Es ist derselbe Status wie der „Hard blocked“-Umschalter im Dashboard** ([Unterkonto blockieren / pausieren](sub-accounts.md#blocking-pausing-a-sub-account)) — ein über die API pausierter Kunde wird im Dashboard als blockiert angezeigt und umgekehrt; das Aufheben der Pause entfernt eine von beiden Seiten gesetzte Blockierung. Der aktuelle Status kann über das Feld `agency_block` unter `GET /v1/subaccounts` (`level` von `"none"`, `"soft_blocked"` oder `"hard_blocked"`) ausgelesen werden.
- **Beide Aufrufe sind idempotent.** Das Pausieren eines bereits pausierten Kunden aktualisiert lediglich die Nachricht, den Grund und den Zeitstempel; das Aufheben der Pause bei einem aktiven Kunden ändert nichts.
- **Der Kunde wird nicht automatisch per E-Mail benachrichtigt** — viele Agenturen nutzen White-Labeling, daher bleibt es Ihnen überlassen, den Kunden zu informieren.
- **Ihre eigene DM Champ-Abrechnung bleibt unberührt.** Das Pausieren eines Kunden wirkt sich nur auf Ihre Beziehung zu diesem aus.
- **KI-Assistenten können dies ebenfalls tun**: Der [MCP-Server](../integrations/connect-ai-clients.md) stellt diese Endpunkte als `pause_subaccount`- und `unpause_subaccount`-Tools bereit.

***

## Credits direkt gewähren oder abziehen

`POST /v1/subaccounts/credits`

Fügt dem Guthaben eines Unterkontos einen exakten Betrag hinzu oder entfernt ihn – das API-Äquivalent zur manuellen Guthabenanpassung im Dashboard. Dies ist eine einmalige Guthabenänderung, die sich von den wiederkehrenden Einstellungen `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months` und `rollover_expiry_days` auf [`PUT /v1/subaccounts/{subAccountUid}/limits`](#set-an-exact-team-member-limit-for-a-client) unterscheidet.

Dies ist der einzige Endpunkt auf dieser Seite, der das Unterkonto über die **E-Mail-Adresse** anstelle von `sub_account_id` identifiziert.

| Feld | Erforderlich | Beschreibung |
|---|---|---|
| `email` | Ja | Die E-Mail-Adresse des Unterkontos, wie sie unter Ihrer Agentur existiert. |
| `amount` | Ja | Anzahl der Credits (ungleich Null). Positive Werte fügen hinzu, negative ziehen ab. |
| `description` | Nein | Wird bei der Anpassung in der Credit-Historie des Kunden angezeigt. Standardmäßig wird eine allgemeine Zeile "Durch Agentur via API angepasst" verwendet. |

**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" }'
```

**Antwort**

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

Ein negatives `amount`, das das Guthaben unter Null bringen würde, wird mit `400` abgelehnt, wobei Ihnen das verfügbare Guthaben und der Betrag, den Sie abziehen wollten, mitgeteilt wird. Wenn der Kunde über eine eigene Stripe-Abrechnung verfügt (Reselling-Modus), zählt ein hinzugefügter Betrag auch als Credits, die er gekauft hat, sodass er die nächste monatliche Zurücksetzung genauso übersteht wie eine echte Aufladung; bei einem standardmäßig zugewiesenen Kunden wird er stattdessen als Teil seines wiederkehrenden Guthabens behandelt. In jedem Fall handelt es sich um eine einmalige Hinzufügung, daher kürzt eine Übertragungsgrenze oder ein Ablaufdatum, das für das Konto (oder seinen Plan) festgelegt wurde, diese niemals – nur das wiederkehrende Guthaben und die Plan-Credits unterliegen diesen.

***

## Unterhaltungen eines Unterkontos lesen

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

Ermöglicht es Ihnen, eine Überwachungs- oder Support-Ansicht der Unterhaltungen eines Kunden zu erstellen, ohne sich in dessen Konto anzumelden. Listen Sie zuerst die Kontakte mit einer Vorschau der neuesten Nachricht auf und lesen Sie dann den vollständigen Nachrichtenverlauf eines Kontakts.

**Kontakte auflisten**

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

| Abfrageparameter | Erforderlich | Beschreibung |
|---|---|---|
| `pageSize` | Nein | Kontakte pro Seite. Standard 25, maximal 50. |
| `lastActivityAt` | Nein | Paginierungs-Cursor – übergeben Sie das `lastActivityAt` der vorherigen Seite, um fortzufahren. |
| `searchQuery` | Nein | Nach Kontaktname oder Telefonnummer filtern. |

**Antwort**

```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"
  }
}
```

Die Kontakte sind nach der letzten Aktivität sortiert. Blättern Sie weiter mit `lastActivityAt`, solange `hasMore` den Wert `true` hat.

**Nachrichten eines Kontakts lesen**

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

| Abfrageparameter | Erforderlich | Beschreibung |
|---|---|---|
| `pageSize` | Nein | Nachrichten pro Seite. Standard 30, maximal 100. |
| `beforeTimestamp` | Nein | Paginierungs-Cursor – Nachrichten abrufen, die älter sind als dieser ISO-Zeitstempel. |

**Antwort**

```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"
  }
}
```

Die Nachrichten werden beginnend mit der neuesten zurückgegeben; blättern Sie mit `beforeTimestamp` rückwärts durch den Verlauf.

***

## Kreditverbrauch und Kampagnenstatus für Ihr gesamtes Portfolio lesen

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

Zwei Dashboard-ähnliche Zusammenfassungen für jedes von Ihnen verwaltete Unterkonto, um Ihre eigenen Agenturberichte zu erstellen, anstatt jeden Kunden einzeln aufrufen zu müssen.

**Kreditverbrauch**

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

| Abfrageparameter | Erforderlich | Beschreibung |
|---|---|---|
| `from` / `to` | Ja | ISO-Datumsbereich. |
| `subAccountId` | Nein | Weglassen für eine agenturweite Zusammenfassung, eine Zeile pro Unterkonto. Einbeziehen, um in den Detailmodus zu wechseln: Zusammenfassung des Unterkontos plus dessen rohe, paginierte Nutzungsdatensätze. |
| `limitCount` | Nein | Nur Detailmodus. Standard 500, maximal 2000. |
| `startAfterTimestamp` | Nein | Nur Detailmodus – Paginierungs-Cursor. |

```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
  }
}
```

Übergeben Sie `subAccountId`, und die Antwort enthält zusätzlich `records`: einzelne Belastungen mit `amount`, `reason`, `campaignName`, `contactName` und `timestamp`. Bei einem Kunden, der seinen eigenen BYOK-Schlüssel anstelle Ihrer Kredite verwendet, werden die Kosten-/Token-Zahlen zurückgehalten (`costsRedacted: true`) – das ist Telemetrie zu Plattformkosten, die nicht für einen Reseller-Betrachter bestimmt ist.

**Kampagnenstatus**

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

| Abfrageparameter | Erforderlich | Beschreibung |
|---|---|---|
| `pageSize` | Nein | Unterkonten pro Seite. Standard 10, Maximum 50. |
| `lastDocumentId` | Nein | Paginierungs-Cursor. |
| `searchQuery` | Nein | Filtern nach Unterkontoname oder E-Mail. |

```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` markieren Unterkonten, die einen Blick wert sind — zum Beispiel eine pausierte Kampagne oder eine, der kein Kanal zugewiesen ist. Verwenden Sie dies, um ein Dashboard zur Zustandsprüfung für den gesamten Bestand zu erstellen, anstatt jedes Kundenkonto einzeln zu öffnen, um eine ins Stocken geratene Kampagne zu bemerken.

> Für zeitreihenbasierte Nachrichten- und Guthabenaktivitäten über alle Kunden hinweg (eine für Diagramme geeignete Reihe statt einer Momentaufnahme zu einem bestimmten Zeitpunkt), siehe `GET /analytics/agency-rollup` im Analytics-API-Leitfaden.

***

## Ein bereits eingerichtetes Kundenkonto bereitstellen

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

Ein [Snapshot](snapshots.md) ist eine wiederverwendbare Vorlage: ein oder mehrere KI-Agenten sowie deren Wissensdatenbank, Tools und Medien, die aus Ihrem eigenen Konto erfasst wurden. Zwei Endpunkte integrieren dies in Ihren Bereitstellungsprozess.

**Automatisch – jeder neue Kunde erhält es direkt.** Markieren Sie einen Snapshot einmalig als Standard, und jedes Konto, das Sie von da an erstellen, wird damit ausgestattet. Dies gilt für Konten, die über `POST /v1/subaccounts` erstellt wurden, Konten, die Sie im Dashboard erstellen, und Konten, die automatisch erstellt werden, wenn ein Kunde über Ihren Checkout-Link bezahlt.

Suchen Sie zuerst die ID des Snapshots:

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

Legen Sie ihn dann als Standard fest:

```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" }'
```

Das ist die gesamte Integration. Senden Sie `{"snapshot_id": null}`, um sie wieder zu deaktivieren. Sie können dasselbe über das Dashboard tun, indem Sie auf den Stern auf der Seite **Snapshots** klicken.

Um auszulesen, was aktuell markiert ist (z. B. bevor ein Bereitstellungsskript entscheidet, ob eines gesetzt werden soll), gibt `GET /v1/snapshots/default` `{ "success": true, "data": { "default_snapshot_id": "SNAPSHOT_ID" } }` zurück — `null`, wenn nichts markiert ist. `GET /v1/snapshots` (wird verwendet, um die oben genannte ID zu finden) gibt dasselbe `default_snapshot_id` zusammen mit dem vollständigen `snapshots`-Array zurück, sodass die meisten Integrationen nur diesen einen Aufruf benötigen. Die Felder des vollständigen Snapshot-Objekts finden Sie im [Snapshots](snapshots.md)-Leitfaden.

**Auf Abruf – in ein einzelnes Konto installieren.** Nützlich für das Onboarding eines bestehenden Kunden oder um einem Kunden später eine zweite Vorlage bereitzustellen.

```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" }'
```

Lassen Sie `sub_account_id` weg, um es stattdessen in Ihr eigenes Agenturkonto zu installieren. Wie bei den `/subaccounts`-Endpunkten wird das Zielkonto hier im Pfad oder Body angegeben und nicht über den umgebenden `sub_account_id`-Parameter.

Wissenswertes, bevor Sie darauf aufbauen:

- **Installierte Agenten starten im pausierten Zustand.** Verbinden Sie zuerst die Kanäle des Kunden und aktivieren Sie dann den Agenten. Dies gilt sowohl für den automatischen als auch für den On-Demand-Pfad.
- **Die Bereitstellung schlägt aufgrund eines Snapshots nie fehl.** Wenn die Installation nicht abgeschlossen werden kann, wird das Kundenkonto dennoch erstellt und ist nutzbar – es ist lediglich leer, und Sie können den Snapshot nachträglich anwenden.
- **Kanäle, Kalender und OAuth-Verbindungen werden nie kopiert.** Jedes Konto verbindet seine eigenen. Tools, die einen einfachen API-Schlüssel verwenden, funktionieren sofort weiter.
- **Ein zweimaliges Anwenden erstellt eine zweite Kopie.** Es wird nichts überschrieben.

***

## Erstellen Sie die Vorlage selbst über die API

`POST /v1/snapshots` · Agenten, benutzerdefinierte Funktionen und Medien über die API

Der obige Abschnitt beschreibt die Verteilung eines Snapshots, der im Dashboard erstellt wurde. Auch der Erstellungsteil ist verfügbar, sodass der gesamte Kreislauf – das Master-Setup einmal zusammenstellen, erfassen und an jeden Kunden weitergeben – vollständig über Code gesteuert werden kann.

Die Komponenten in der Reihenfolge, in der ein Bereitstellungsskript sie verwendet:

1. **Erstellen Sie Ihre benutzerdefinierten Funktionen.** `POST /v1/custom-functions` erstellt eine; `GET /v1/custom-functions` listet Ihre vorhandenen auf, und `GET`, `PUT` sowie `DELETE` unter `/v1/custom-functions/{customFunctionId}` lesen, aktualisieren und entfernen eine. `POST /v1/custom-functions/test` führt einen Testlauf einer Definition durch, bevor Sie diese speichern.
2. **Erstellen und gestalten Sie den Agenten.** `POST /v1/agents` erstellt ihn, `PUT /v1/agents/{agentId}` aktualisiert ihn, und `PATCH /v1/agents/{agentId}/active` mit `{ "active": false }` hält ihn während der Bearbeitung angehalten (der gleiche Aufruf mit `true` schaltet ihn live). `GET /v1/agents` listet sie auf.
3. **Geben Sie dem Agenten seine Fähigkeiten.** `POST /v1/agents/{agentId}/custom-functions` mit `{ "custom_function_id": "..." }` weist dem Agenten eine Funktion zu; das entsprechende `DELETE /v1/agents/{agentId}/custom-functions/{customFunctionId}` entfernt sie wieder.
4. **Füllen Sie die Medienbibliothek.** `POST /v1/agents/{agentId}/media-library` lädt ein Element hoch (JSON mit `base64Data`, `mimeType`, `title`, `description`); `GET` listet die Elemente des Agenten auf, und `PATCH`/`DELETE` unter `/{itemId}` aktualisieren oder entfernen eines.
5. **Erfassen Sie es als Snapshot.**

```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
  }'
```

Von dort aus geht es weiter wie im vorherigen Abschnitt: Markieren Sie ihn als Standard, damit jeder neue Client damit ausgestattet wird, oder wenden Sie ihn bei Bedarf an. Die Verwaltung erfolgt parallel dazu: `PATCH /v1/snapshots/{snapshotId}` mit `{ "name": "..." }` benennt einen um, `DELETE /v1/snapshots/{snapshotId}` löscht einen (und entfernt die Standard-Markierung, falls vorhanden), und `GET /v1/snapshots/apply-targets` listet jedes Konto auf, in dem Sie eine Installation vornehmen könnten.

Die Endpunkte für Agenten, benutzerdefinierte Funktionen und Medien akzeptieren alle `sub_account_id`, sodass dieselben Aufrufe auch einen Agenten direkt im Konto eines Clients verwalten können. Snapshot-Aufrufe wirken sich immer auf Ihr Agenturkonto aus – die Vorlage verbleibt bei Ihnen. Vollständige Anforderungs- und Antwortschemas für all diese finden Sie in der [API-Referenz](../api/reference.md).

***

## Verwalten Sie Ihre Preisebenen über die API

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

Die Pläne, die Sie im **SaaS-Modus → Preisebenen** verkaufen, können per Code gelesen und geändert werden, sodass Ihr eigenes Admin-Panel oder Bereitstellungsskript einen Plan hinzufügen, einen Preis anpassen oder einen Checkout-Link bereitstellen kann, ohne dass jemand das Dashboard öffnen muss. Authentifizieren Sie sich mit Ihrem Agentur-API-Schlüssel wie bei jedem anderen Aufruf auf dieser Seite; diese Endpunkte sind auf Agenturebene angesiedelt und erfordern daher kein `sub_account_id`. Jeder Schreibvorgang führt dieselbe Validierung und denselben Stripe-Produkt-und-Preis-Abgleich durch wie das Speichern im Dashboard, sodass ein hier erstellter Plan von einem manuell eingerichteten nicht zu unterscheiden ist.

### Listen Sie Ihre Ebenen auf

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

**Antwort**

```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
  }
}
```

Jede Ebene wird mit ihrer **`tierIndex`** zurückgegeben — ihrer Position in Ihrer Planliste, über die die anderen drei Aufrufe sie adressieren — und einem sofort einsatzbereiten **`checkout_url`**, demselben Link, den Ihnen der Reiter **Zahlungen** gibt, der bereits auf die [White-Label-Domain](white-labeling.md) verweist, auf der dieser Plan verkauft wird.

### Fügen Sie eine Ebene hinzu

Der Body besteht aus einem Ebenen-Objekt; es wird an das Ende Ihrer Liste angehängt.

```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"]
  }'
```

Die Antwort enthält die erstellte Ebene, einschließlich der `tierIndex`, auf der sie gelandet ist, und ihrer `checkout_url`.

### Bearbeiten Sie eine Ebene

Senden Sie nur die Felder, die Sie ändern möchten; alles andere am Plan bleibt unverändert.

```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 }'
```

Ein Feld, das die API nicht erkennt, wird abgelehnt, anstatt ignoriert zu werden, und der Fehler benennt es — so kann ein Tippfehler niemals stillschweigend eine Einstellung schreiben, die aktiv aussieht, aber nichts bewirkt. Das Ändern des Preises, der Credits, der Währung oder des Abrechnungszyklus erstellt einen neuen Preis in Ihrem Stripe; Kunden, die bereits abonniert haben, behalten das, wofür sie sich angemeldet haben.

### Löschen Sie eine Ebene

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

Dieselbe Regel wie im Dashboard: Ein Plan, der noch aktive Abonnenten hat, kann nicht gelöscht werden. Die Anfrage wird abgelehnt und teilt Ihnen mit, wie viele Abonnenten sich darauf befinden — kündigen oder migrieren Sie diese zuerst. Ein erfolgreiches Löschen antwortet mit Ihren verbleibenden Ebenen, die bereits neu nummeriert sind.

### Die Felder einer Ebene

| Feld | Was es ist |
|---|---|
| `label` / `description` | Der Name des Plans und die optionale Zeile, die auf Ihrer Checkout-Seite angezeigt wird. |
| `credits` | Credits, die der Kunde **pro Monat** bei einem monatlichen oder jährlichen Plan erhält, und **pro Abrechnungszeitraum** bei einem wöchentlichen Plan. |
| `price_cents` | Preis pro Abrechnungsintervall in der kleinsten Währungseinheit (`2900` = $29.00). Bei einem Jahresplan ist dies der Preis für das gesamte Jahr. |
| `currency` | ISO-Code in Kleinbuchstaben – `usd`, `eur`, `gbp` und so weiter. |
| `billing_interval` / `billing_interval_count` | `month` (Standard), `year` oder `week` mit einer Anzahl von 1–52 für "alle N Wochen". |
| `trial_days` | Länge der kostenlosen Testversion, 0 bis 90. `0` (oder das Weglassen) bedeutet keine Testversion. |
| `trial_credits` | Credits, mit denen der Kunde die Testversion beginnt. Standardmäßig der `credits` des Plans. |
| `trial_card_required` | `false` ermöglicht es dem Kunden, die Testversion ohne Eingabe einer Karte zu starten. Standardmäßig `true`. |
| `trial_hard_expiry` | `true` gibt ungenutzte Test-Credits an Ihren Pool zurück und sperrt das Konto des Kunden, wenn eine Testversion ohne Upgrade endet. Standardmäßig `false` – siehe [Harter Ablauf nach Testversion](agency-accounts.md#step-3--set-up-pricing-tiers). |
| `rollover_cap_months` | Monate des Guthabens, die Kunden in diesem Plan zwischen Verlängerungen mitnehmen können – eine Zahl von 0 bis 120, Brüche sind erlaubt. `0` überträgt nichts; `null` (Standard) bedeutet keine Begrenzung. Siehe [Begrenzung der Übertragung](sub-accounts.md#capping-what-rolls-over). |
| `rollover_expiry_days` | Tage, nach denen ungenutzte Credits bei der nächsten Verlängerung verfallen – eine ganze Zahl von 1 bis 3650. `null` (Standard) bedeutet, dass sie niemals ablaufen. |
| `features` / `feature_settings` | Was Kunden in diesem Plan erhalten – dieselben Feature-IDs wie bei [Wählen Sie, welche Kanaltypen ein Kunde verbinden kann](#choose-which-channel-types-a-client-can-connect). |
| `team_seats_limit` | Team-Plätze, die der Plan gewährt: eine exakte Zahl, `0` für keine, `-1` für unbegrenzt. |
| `white_label_config` | Auf welcher Ihrer [White-Label-Domains](white-labeling.md#up-to-three-white-labels) der Plan verkauft wird. |

Die Testphasen-Felder sind nur für Pläne mit einer Testphase relevant: Wenn Sie einen Tarif mit `trial_days: 0` speichern, werden sie verworfen. Die Stripe-Produkt- und Preis-IDs des Plans werden für Sie verwaltet und können nicht manuell festgelegt werden.

Drei Dinge, die Sie beachten sollten:

- **Tarif-Indizes sind Positionen, keine permanenten IDs.** Das Löschen eines Tarifs verschiebt alle nachfolgenden Tarife um eine Position nach oben. Rufen Sie die Liste daher nach jeder Änderung erneut ab – und kopieren Sie die Checkout-Links, die Sie veröffentlicht haben, neu, genau wie Sie es nach dem Löschen eines Tarifs im Dashboard tun würden.
- **Der SaaS-Modus muss zuerst eingerichtet werden.** Diese Endpunkte erfordern ein Agenturkonto mit White-Labeling und einem bereits gespeicherten Stripe-Schlüssel; ohne diesen gibt es kein Stripe-Konto, auf dem das Produkt und der Preis des Tarifs hinterlegt werden können.
- **Zwanzig Tarife sind das Limit**, genau wie im Dashboard. Das Feld `max_tiers` in der Listenantwort zeigt Ihnen das aktuelle Limit an.

Vollständige Anforderungs- und Antwort-Schemas finden Sie in der [API-Referenz](../api/reference.md) unter **Agency**.

***

## Legen Sie Ihren Preis pro Credit über die API fest

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

Der Preis, den Kunden für Ad-hoc-Aufladungen zahlen (**SaaS-Modus → Preis pro Credit**), kann auch per Code ausgelesen und geändert werden. Dies ist für Fälle gedacht, in denen sich der Preis automatisch anpassen muss: Eine Agentur, die Credits in einer Währung verkauft, aber in einer anderen abrechnet, kann den Preis durch einen geplanten Job bei Wechselkursänderungen anpassen lassen, anstatt dass jemand dies jede Woche manuell erledigen muss.

### Aktuellen Preis abrufen

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

**Antwort**

```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` ist der niedrigste Preis, den die Plattform in dieser Währung zulässt, sodass ein Job einen neuen Preis prüfen kann, bevor er ihn übermittelt. Alle drei Werte sind `null`, bis ein Preis festgelegt wurde.

### Preis ändern

```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" }'
```

Senden Sie nur das, was Sie ändern möchten. `price_per_credit_cents` ist der Preis in der kleinsten Währungseinheit (`130` = R$1,30); `currency` ist ein ISO-Code in Kleinbuchstaben; `note` ist eine optionale Zeile mit bis zu 200 Zeichen, die Kunden direkt unter dem Preis pro Credit auf ihrer Abrechnungsseite angezeigt wird – nützlich für einen Referenzpreis in einer anderen Währung, wie z. B. *"USD 0,25 pro Credit zu unserem Referenzkurs"*. Senden Sie `"note": ""`, um sie zu entfernen. Die Antwort hat das gleiche Format wie beim Abruf oben, sodass ein Job vergleichen und den Schreibvorgang überspringen kann, wenn sich nichts geändert hat.

Es gelten dieselben Regeln wie im Dashboard: Der Preis darf das Plattform-Minimum für diese Währung nicht unterschreiten und das Konto benötigt White Labeling. Im Gegensatz zu den Endpunkten für Preisstufen ist kein Stripe-Schlüssel erforderlich, um diesen Wert zu lesen oder zu ändern.

### Geben Sie einem Job einen Schlüssel mit eingeschränkten Rechten

Die Eingabe Ihres vollständigen Agenturschlüssels in einen Scheduler gewährt mehr Zugriff, als für eine Preisaktualisierung erforderlich ist. Erstellen Sie stattdessen einen **bereichsbeschränkten Schlüssel** (scoped key), der auf den Bereich **Agentur-Guthabenpreis** begrenzt ist: Dieser Schlüssel kann nur den Preis pro Guthaben lesen und ändern – er hat keinen Zugriff auf Unterkonten, Pläne, Guthaben oder Ihre Stripe-Verbindung.

```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"] } }'
```

Die Antwort enthält den neuen Schlüssel `api_key` **einmalig** – er wird danach nie wieder angezeigt, speichern Sie ihn also sofort. Setzen Sie `"read_only": true` für einen Schlüssel, der den Preis nur lesen muss, und fügen Sie `"expires_at"` (ein ISO-Datum) hinzu, wenn er automatisch ablaufen soll. Nur der Schlüssel des Kontoinhabers kann bereichsbeschränkte Schlüssel erstellen; listen oder widerrufen Sie diese mit `GET /v1/api-keys` und `DELETE /v1/api-keys/{id}`.

***

## Ein Unterkonto Ihre Preisgestaltung lesen lassen

`GET /v1/subaccounts/agency-pricing`

Jeder andere Endpunkt auf dieser Seite wird mit **Ihrem Agenturschlüssel** aufgerufen, wobei optional ein Kunde über `sub_account_id` adressiert wird. Dieser hier ist das Gegenteil: Er wird mit dem **eigenen API-Schlüssel des Unterkontos** aufgerufen, ohne `sub_account_id`, sodass die eigene Aufladeseite eines Kunden (oder eine Integration, die Sie für ihn erstellen) anzeigen kann, was Sie ihm berechnen, ohne jemals Ihr Agenturkonto zu sehen.

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

**Antwort**

```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"
  }
}
```

Dies spiegelt genau das wider, was [`GET /v1/agency/pricing-tiers`](#list-your-tiers) und [`GET /v1/agency/credit-price`](#read-the-current-price) für Sie als Agentur zurückgeben, abzüglich dessen, was der Kunde nicht sehen muss (Stripe-IDs, `max_tiers` usw.). Es funktioniert nur für ein Konto, das tatsächlich ein Unterkonto mit einer verknüpften Agentur ist — ein Aufruf von Ihrem eigenen Agenturkonto aus führt zu einem Berechtigungsfehler.

***

## Wichtige Hinweise

- **Verwenden Sie Ihren Agenturschlüssel.** Authentifizieren Sie jeden Aufruf mit dem API-Schlüssel Ihres Agenturkontos – nicht mit dem des Unterkontos. Der Parameter `sub_account_id` leitet die Aktion weiter.
- **Guthaben wird vom Unterkonto abgebucht.** Käufe und wiederkehrende Gebühren belasten das Guthaben des jeweiligen Unterkontos, nicht Ihres.
- **Ein `404` bedeutet „nicht Ihr Unterkonto“.** Überprüfen Sie die ID und stellen Sie sicher, dass es sich um ein Konto handelt, das Sie verwalten.
- **Der Parameter ist überall dort optional, wo er akzeptiert wird.** Wenn Sie ihn weglassen, wirkt der Endpunkt auf Ihr Agenturkonto, sodass Sie eine Integration für beides verwenden können.

***

## Verwandte Themen

- [API-Zugriff](../integrations/api-access.md) — Authentifizierung, Basis-URL, Fehler, Ratenbegrenzungen.
- [Unterkonten](sub-accounts.md) — Auflisten und Verwalten der Konten, die Sie adressieren können.
- [Automatische Aufladung von Unterkonten](sub-account-auto-recharge.md) — Gewähren Sie einem Unterkonto Guthaben per Webhook + API.
- [Kampagnen-API](../api/campaigns.md) — Erstellen, Aktualisieren und Kopieren von Kampagnen, einschließlich der vollständigen Feldreferenz.
- [Kanalverbindungs-API](../api/channels.md) — Verbinden Sie die Kanäle eines Kunden und leiten Sie sie an eine Kampagne weiter.
- Analytics-API-Leitfaden (im API-Bereich) — das Rollup für Agentur-Unterkonten und alle anderen Reporting-Endpunkte.
- [Snapshots](snapshots.md) — was ein Snapshot erfasst und wie man einen im Dashboard erstellt.
