
# API per le agenzie

Come agenzia, puoi utilizzare la stessa API REST usata dai tuoi clienti, ma indirizzando le singole richieste a uno dei tuoi **sotto-account** gestiti invece che al tuo account. Questo ti permette di creare strumenti che gestiscono l'onboarding di un cliente dall'inizio alla fine: creando le sue campagne, addestrando la sua IA su una base di conoscenza, importando i suoi contatti, collegando i suoi canali di messaggistica e acquistando numeri di telefono, il tutto senza dover accedere manualmente a ogni sotto-account.

Questa pagina tratta solo il comportamento specifico per le agenzie: come agire per conto di un sub-account con il parametro `sub_account_id`. Per le nozioni di base (generazione di una chiave, autenticazione, URL di base, formato degli errori, limiti di frequenza), inizia dalla guida [Accesso API](../integrations/api-access.md). Tutto ciò che è riportato lì si applica anche qui: l'autenticazione avviene con la chiave API del **tuo account agenzia**.

::: note
**Nota:** Questa pagina è tecnica. Se non sei uno sviluppatore, condividila con la persona che sta creando la tua integrazione.
:::


***

## Come funziona l'"agire per conto di"

Per impostazione predefinita, ogni richiesta API agisce sull'account che possiede la chiave API, ovvero il tuo account agenzia. Per agire invece su un account cliente gestito, aggiungi alla richiesta il parametro opzionale `sub_account_id`, impostandolo sull'id dell'account di quel cliente.

- **Ometti `sub_account_id`** → la richiesta agisce sul tuo account agenzia.
- **Includi `sub_account_id`** → la richiesta agisce su quel sub-account, ma solo dopo che la piattaforma ha confermato che il sub-account è effettivamente tuo.

Ti autentichi sempre con la chiave API del **tuo account agenzia**. Non hai mai bisogno della chiave del sub-account e non gestisci mai le credenziali del sub-account.

### Dove inserirlo

- **Endpoint GET / DELETE** → passalo come parametro di query: `?sub_account_id=THE_SUB_ACCOUNT_ID` (insieme al tuo `apiKey`, se ti autentichi tramite query).
- **Endpoint POST / PUT / PATCH** → includilo nel corpo della richiesta JSON come `"sub_account_id": "THE_SUB_ACCOUNT_ID"`.
- **Assistenti IA** → nulla da configurare. Il [server MCP](../integrations/connect-ai-clients.md) mantiene la stessa impostazione sui suoi strumenti di lettura, quindi una connessione con la tua chiave di agenzia può generare report su ogni cliente: basta nominare il cliente nella tua richiesta ("quanti contatti ha Bella's Bistro?"). Sono disponibili anche azioni di scrittura: ogni endpoint che accetta `sub_account_id` viene esposto come strumento, così puoi creare, modificare e inviare per conto di un cliente dalla stessa connessione.

### Trovare l'id di un sub-account

Il `sub_account_id` è l'id univoco dell'account cliente. Puoi ottenere l'elenco dei tuoi sotto-account e i relativi id dagli endpoint dell'API **SubAccounts** (consulta la guida [Sub-Accounts](sub-accounts.md)) o dalla pagina **Sub Accounts** nella barra laterale.

***

## La proprietà viene sempre verificata

Quando passi un `sub_account_id`, la piattaforma verifica che l'account sia un sub-account reale **e** che appartenga alla tua agenzia. Solo allora la richiesta viene elaborata.

Se l'id è sconosciuto, non è un sub-account o appartiene a un'altra agenzia, la richiesta fallisce con una risposta **`404`**:

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

> **Perché 404 e non 403?** Una risposta "forbidden" (vietato) comunicherebbe a un estraneo che l'id esiste ma non gli appartiene. Restituire lo stesso `404` per "non esiste" e "non è tuo" significa che l'endpoint non può essere utilizzato per scoprire quali id account appartengono ad altre agenzie. Considera un `404` qui come "questo non è un sub-account che gestisci."

***

## Dove è supportato `sub_account_id`

`sub_account_id` è accettato praticamente su ogni endpoint di **risorsa**: qualsiasi chiamata che crea, legge, aggiorna o elimina i dati di un account. In pratica, puoi eseguire il provisioning e gestire l'intera configurazione di un sotto-account con la tua chiave di agenzia:

- **Configurazione IA** — campagne, agenti, FAQ, fonti della base di conoscenza (scansione sito web **e** caricamento documenti), gruppi della base di conoscenza, trasmissioni, funzioni personalizzate, server MCP
- **Contatti e CRM** — contatti (inclusa l'importazione), liste, tag, attività, trattative, appuntamenti, eventi
- **Canali e numeri** — collega WhatsApp / WhatsApp Web / Telegram / Instagram & Messenger / LINE, cerca / acquista / gestisci numeri di telefono, modelli WhatsApp, instradamento canali
- **Messaggistica e contenuti** — invia messaggi, sessioni di chat, esportazioni chat, riepiloghi giornalieri
- **Impostazioni e integrazioni** — webhook, configurazione widget chat, configurazione white-label, SMS BYOK e altre impostazioni dell'account, analisi

Per ognuno di questi, il parametro è **facoltativo**: se lo ometti, la chiamata agirà sul tuo account di agenzia, quindi un'unica integrazione serve entrambi. Crediti e utilizzo provengono sempre dall'account a cui ti rivolgi: gli addebiti per campagne, messaggi, tag e numeri di un sotto-account incidono sul saldo **del sotto-account**.

### Dove NON si applica

Alcuni endpoint sono a livello di agenzia o auto-indirizzati e ignorano `sub_account_id`:

- **Gestione dei sotto-account stessi** — gli endpoint SubAccounts (creazione / elenco / aggiornamento di un sotto-account) e l'endpoint per il limite di spesa BYOK indicano già il sotto-account nel proprio percorso URL. Gli [endpoint di prezzi e policy](#set-per-client-ai-pricing-and-policy) e gli [endpoint di monitoraggio chat](#read-a-sub-accounts-conversations) seguono lo stesso schema.
- **Copia di un Agente tra account** — `POST /v1/subaccounts/agents/copy` indica entrambi gli account, prendendo la destinazione come `targetUserId`. Vedi l'[esempio pratico](#worked-example-ship-a-template-agent-into-every-new-client) qui sotto. (Il vecchio `POST /v1/subaccounts/campaigns/copy` funziona allo stesso modo ma è deprecato insieme al resto della [Campaigns API](../api/campaigns.md).)
- **Regolazione dei crediti e i due rollup a livello di agenzia** — [`POST /v1/subaccounts/credits`](#grant-or-deduct-credits-directly) identifica il sotto-account tramite `email`; [`GET /v1/subaccounts/credit-usage`](#read-credit-usage-and-campaign-health-across-your-book) e `GET /v1/subaccounts/campaign-status` generano report su ogni sotto-account contemporaneamente, quindi non c'è un singolo account da gestire.
- **Il tuo account di agenzia** — La gestione delle chiavi API, i report sull'utilizzo dell'agenzia, la gestione del team e i tuoi [livelli di prezzo](#manage-your-pricing-tiers-over-the-api) agiscono sempre sul tuo account di agenzia.
- **Webhook per messaggi in entrata** — gli endpoint in cui i sistemi esterni inviano dati *verso* l'interno sono legati all'account le cui credenziali li hanno configurati, quindi non c'è nulla da reindirizzare.

> L'elenco sempre aggiornato e leggibile dalle macchine dei parametri accettati da ciascun endpoint si trova nel riferimento API della tua dashboard (**Settings → Integrations → API Key**) e nella specifica OpenAPI all'indirizzo `GET /v1/docs/openapi.yaml`. Rilasciamo spesso modifiche alle API: considerale come la fonte di verità.

::: master-only
<figure><img src="../.gitbook/assets/v2-api-access-key-section.png" alt="Pagina delle impostazioni della chiave API con chiave mascherata e controllo Rigenera"><figcaption><p>Impostazioni → Integrazioni → Chiave API — la chiave della tua agenzia si trova qui, insieme al link al riferimento completo dell'API.</p></figcaption></figure>
:::

***

## Esempio pratico: connettere Instagram e Messenger per un sub-account

La connessione di Instagram e Messenger è un flusso basato su browser. Si avvia tramite API, si fornisce l'URL di consenso restituito al cliente (o lo si apre per lui), si attende che autorizzi nel suo browser, quindi si sceglie quale pagina connettere — il tutto puntando al suo sub-account con `sub_account_id`.

### Passaggio 1 — Avviare la connessione

Chiama l'endpoint di connessione con il `sub_account_id` del cliente nel body. Qui non vengono inviate credenziali; la piattaforma restituisce un URL di consenso che il cliente deve aprire in un browser, oltre a un token di correlazione monouso.

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

**Risposta:**

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

Invia il cliente a `oauth_url` in un browser per autorizzare. Il `state_token` correla questo tentativo ed è un segreto a breve termine — non registrarlo. Il tentativo scade a `expires_at`; se scade, ricomincia.

### Passaggio 2 — Eseguire il polling fino al caricamento delle pagine

Dopo che il client ha autorizzato, esegui il polling dell'endpoint di stato (con lo stesso `sub_account_id`, questa volta come parametro di query) finché non appaiono le pagine collegabili.

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

**Risposta:**

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

Il campo `status` passa attraverso `pending` → `token_received` → `pages_loaded` → `connected`. Attendi `pages_loaded` prima di selezionare una pagina. Possono apparire anche due stati di errore terminale invece di procedere: `failed` e `expired` (il cliente ha rifiutato il consenso o la finestra di circa 30 minuti del token di stato è scaduta) — un campo `reason` viene incluso quando si verifica uno di questi casi. Interrompi il polling e riavvia dal passaggio 1 se ne vedi uno; non attendere `pending` all'infinito. I token di accesso alla pagina non vengono mai restituiti.

### Passaggio 3 — Seleziona la pagina da collegare

Scegli uno degli ID pagina dal Passaggio 2 e selezionalo. Selezionando una pagina si collegano sia Instagram che Messenger per quella pagina. Includi nuovamente `sub_account_id` nel corpo.

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

**Risposta:**

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

Ecco fatto: Instagram e Messenger sono ora collegati al sub-account del client. Hai fornito solo `page_id`; la credenziale sottostante viene risolta sul server e non passa mai attraverso la tua integrazione.

***

## Esempio pratico: acquisto di un numero per un sub-account

L'acquisto di un numero funziona allo stesso modo: cerca con `sub_account_id` nella query, quindi acquista inserendolo nel corpo. I crediti vengono detratti dal saldo **del sub-account** e il numero viene fornito sul sub-account.

**Ricerca (cURL):**

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

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

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

**Risposta:**

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

Il numero viene fornito nello stato `PURCHASED` e la registrazione del mittente WhatsApp continua in background. Esegui il polling di `GET /v1/phone-numbers/{phoneNumber}/status?sub_account_id=abc123def456` finché lo stato non raggiunge `ONLINE` prima di inviare.

***

## Esempio pratico: inviare un Agente modello a ogni nuovo cliente

Il modello tipico di un'agenzia consiste nel mantenere un Agente principale sull'account dell'agenzia, configurato come si desidera che ogni cliente inizi, e crearne una copia in ogni nuovo sotto-account al momento del provisioning. Si tratta di tre chiamate e non è necessario ripetere nulla in seguito: la copia mantiene le sue impostazioni finché non le modifichi.

### Passaggio 1 — Copiare l'Agente

`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 risposta contiene l'id del nuovo Agente in `data.agent_id`. Le FAQ, la knowledge base e la libreria multimediale vengono trasferite; i modelli WhatsApp, i post social collegati e i contatti dell'account di origine non vengono trasferiti intenzionalmente. Elenco completo dei campi nell'[AI Agents API](../api/agents.md#copy-an-agent-into-a-sub-account-agencies).

Nota che questo endpoint utilizza `targetUserId` invece di `sub_account_id`: indica entrambi gli account. Le due chiamate seguenti utilizzano il normale parametro `sub_account_id`.

### Passaggio 2 — Attivarlo

La copia arriva sempre in pausa, quindi non può inviare messaggi a nessuno finché non lo decidi tu. Questo è anche il momento di fissare il livello AI che vuoi assegnare al cliente; rimarrà impostato, quindi non c'è bisogno di riapplicarlo secondo una pianificazione.

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

Per impedire al cliente di modificare il livello in seguito, [blocca i livelli consentiti](#set-per-client-ai-pricing-and-policy) sul sotto-account invece di inviare nuovamente il valore.

### Passaggio 3 — Collegamento dei canali del cliente

La copia arriva anche senza routing, quindi nulla la raggiunge finché non la rendi il risponditore sui canali che il cliente ha collegato. Una chiamata per canale:

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

Da qui, un primo messaggio da un contatto sconosciuto su quel canale viene intercettato automaticamente dall'Agente copiato. Vedi [Indirizzare un canale verso un Agente](../api/entry-points.md#point-a-channel-at-an-agent) per gli altri canali e il routing per numero.

> **Imposta il fuso orario del cliente quando crei il sotto-account.** Passa `time_zone_id` su `POST /v1/subaccounts`. Gli orari di attività della campagna vengono valutati nel fuso orario del sotto-account, quindi un cliente creato senza di esso vedrà la sua pianificazione letta rispetto all'UTC — il che sposta silenziosamente gli orari in cui all'assistente è consentito rispondere.

***

## Ignora la procedura guidata di configurazione per un cliente che configuri personalmente

`POST /v1/subaccounts`

Per impostazione predefinita, la prima volta che il proprietario di un nuovo sub-account effettua l'accesso, viene guidato attraverso la procedura guidata di configurazione (Setup Wizard). Per i clienti "chiavi in mano" — dove sei tu a creare la campagna e a collegare i canali prima ancora che il cliente acceda — passa `guided_onboarding: false` al momento della creazione dell'account. Il cliente verrà indirizzato direttamente alla dashboard e la voce **Setup Wizard** sarà nascosta dalla sua barra laterale.

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

Ometti il campo (o invia `true`) e la procedura guidata si comporterà esattamente come ha sempre fatto, quindi le integrazioni esistenti non necessitano di modifiche. Per restituire la procedura guidata a un cliente in un secondo momento, mostra nuovamente l'elemento `guided_onboarding` con `PUT /v1/subaccounts/{subAccountUid}/menu-visibility` (di seguito) — la visibilità del menu controlla se la procedura guidata è raggiungibile, `guided_onboarding` controlla solo il reindirizzamento al primo accesso.

***

## Disattiva Attività, Riepiloghi giornalieri o Libreria multimediale per un cliente

`POST /v1/subaccounts`

Queste tre funzioni sono attive per ogni nuovo cliente a meno che non venga specificato diversamente, e si comportano in modo diverso rispetto a qualsiasi altra funzionalità in questa guida: sono **opt-out**, non opt-in. Escluderle da `features` non è sufficiente di per sé, perché l'elenco `features` di un'integrazione precedente semplicemente non le menzionava: non possiamo distinguere tra "l'agenzia ha disattivato questa funzione" e "questo elenco è stato scritto prima che l'opzione esistesse".

Quindi dichiaralo esplicitamente con `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
    }
  }'
```

Ogni chiave è facoltativa; tutto ciò che viene omesso rimane attivo. Con `tasks: false` l'IA smette di creare attività per quel cliente e non vengono inviate email di "Nuova attività creata"; con `daily_summaries: false` il riepilogo notturno non viene mai generato né inviato via email.

`feature_settings` è l'unica cosa che disattiva queste tre funzioni al momento della creazione. Escluderle da `features` non ha alcun effetto di per sé, indipendentemente da come appare il resto dell'elenco: è una scelta deliberata, affinché un'integrazione precedente non perda silenziosamente tutte e tre le funzioni.

Per modificare tutto ciò in un secondo momento, invia l'elenco `features` completo a `PUT /v1/subaccounts/{subAccountUid}/features`: in quel caso, la presenza nell'elenco attiva una funzionalità e l'assenza la disattiva.

***

## Effettua l'auto-login dei tuoi clienti nel loro sotto-account (SSO)

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

Una chiamata con la chiave API della tua agenzia restituisce un URL pronto all'uso che collega il cliente direttamente al proprio sotto-account: nessuna schermata di login, nessun passaggio per la password, nulla da sviluppare. Aprilo in una nuova scheda, tramite un redirect o in un iframe all'interno del tuo prodotto.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `redirect` | No | Pagina in-app su cui vuoi che il client approdi, ad es. `"/chats"` o `"/agents"`. Restituito come `deep_link_url` nella risposta. |
| `app_base_url` | No | Host della dashboard per il link. Il valore predefinito è il dominio della tua app white-label (o il dominio della piattaforma se non ne hai uno). Deve essere `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" }'
```

**Risposta**

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

Come utilizzarlo al meglio:

- **Un solo passaggio.** L'apertura di `url` autentica il cliente e lo indirizza direttamente alla pagina `redirect` della dashboard: nessuna schermata di login, nessuna pagina intermedia. `deep_link_url` indica la stessa destinazione, per gli integratori che preferiscono navigare esplicitamente in un frame dopo il login; una volta creata la sessione, qualsiasi percorso della dashboard funziona in quel contesto del browser.
- **Generazione su richiesta, apertura immediata.** Il link contiene una credenziale di accesso e scade dopo circa un'ora. Richiedilo lato server nel momento in cui il cliente clicca e non memorizzarlo né inviarlo mai via email.
- Il token di accesso viaggia nel frammento dell'URL (`#…`), che i browser non inviano mai ai server, e viene rimosso dalla barra degli indirizzi nel momento in cui viene utilizzato.
- **Solo i tuoi sotto-account.** L'endpoint rifiuta qualsiasi account che non sia di proprietà della tua agenzia.
- Un link scaduto mostra un errore chiaro con un percorso per riprovare: generane uno nuovo.

***

## Nascondi elementi di navigazione su un sotto-account

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

Controlla quali elementi della barra laterale e delle impostazioni vede un sotto-account: utile quando incorpori la dashboard e desideri solo le superfici che il tuo prodotto non copre già. Tutto ciò che non è elencato rimane visibile; invia `null` come intero valore `menuVisibility` per ripristinare la visibilità di tutto. Nascondere un elemento nasconde la voce di menu: abbinalo alle funzionalità che concedi al sotto-account per un controllo rigoroso.

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

**Risposta**

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

`side_nav` accetta queste 13 chiavi, che corrispondono ai nomi delle voci della barra laterale: `Dashboard`, `DailySummaries`, `Chats`, `Contacts`, `Deals`, `Tasks`, `Automations`, `Campaigns`, `Appointments`, `Settings`, `Help`, `CreditsCounter` (il saldo del credito mostrato nella barra laterale) e `guided_onboarding` (la procedura guidata di configurazione). Sono accettate altre tre chiavi — `AiInsights`, `Sub Accounts` e `Agency Reselling` — che però non eseguono alcuna azione: si applicavano solo alla dashboard classica ormai in disuso, pertanto la loro impostazione non ha alcun effetto sui tuoi sotto-account. Le chiavi mancanti indicano che l'elemento è visibile; quando accedi personalmente al sotto-account, gli elementi nascosti vengono mostrati temporaneamente in modo da poter sempre ripristinare le impostazioni.

Nascondere una pagina dal menu non ne garantisce mai l'accesso. `Automations` richiede che la funzionalità `automations` sia concessa sul sotto-account: imposta la chiave su `true` senza di essa e la pagina non apparirà comunque. `Tasks` e `DailySummaries` funzionano al contrario: sono attive per ogni cliente a meno che non vengano disattivate (vedi [Disattiva Attività, Riepiloghi giornalieri o Libreria multimediale per un cliente](#turn-tasks-daily-summaries-or-the-media-library-off-for-a-client)).

***

## Scegli quali tipi di canale un client può connettere

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

Gli interruttori **Tipi di canale** che vedi su un livello di piano sono normali ID funzionalità, quindi puoi impostarli per client dall'API invece che dalla dashboard. Questo è uno degli endpoint che nomina il sub-account nel proprio URL, quindi non richiede alcun `sub_account_id`.

| ID funzionalità | Canale |
|---|---|
| `channel_chat_widget` | Widget chat sito web |
| `channel_whatsapp_api` | API WhatsApp Business |
| `channel_whatsapp_web` | WhatsApp Web (numero collegato tramite QR) |
| `channel_instagram` | Instagram |
| `channel_messenger` | Facebook Messenger |
| `channel_telegram` | Telegram |
| `channel_line` | LINE |
| `channel_viber` | Viber |
| `channel_email` | Casella di posta elettronica |
| `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"
    ]
  }'
```

Tre cose da fare correttamente:

- **La chiamata sostituisce l'intero elenco delle funzionalità.** Invia ogni funzionalità che il client deve mantenere, non solo quelle che stai modificando. Gli stessi ID funzionano come `features` su `POST /v1/subaccounts` quando crei l'account.
- **I tipi di canale e il numero di canali sono blocchi separati ed entrambi si applicano.** `channels_1` / `channels_3` / `channels_unlimited` controllano *quante* connessioni; gli ID `channel_*` controllano *quali tipi*. L'esempio sopra significa "fino a 3 connessioni e solo Widget chat, WhatsApp Web o Instagram".
- **Non inviare alcun ID `channel_*` significa nessuna restrizione sui canali.** Questo è il comportamento originale, motivo per cui i client esistenti non sono stati influenzati quando è stata rilasciata questa funzione. Inviandone uno o più, tutto il resto verrà visualizzato come bloccato nella pagina Canali del client con una nota di aggiornamento invece di un pulsante Connetti. I canali che il client ha già connesso continueranno a funzionare.

> L'impostazione dell'elenco dei canali su un **livello di piano**, in modo che ogni client che acquista quel livello lo erediti, viene eseguita nella dashboard nelle impostazioni del tuo piano di agenzia. Questo endpoint lo imposta su uno specifico sub-account.

***

## Imposta un limite esatto di membri del team per un cliente

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

Le funzionalità `team_seats_*` offrono solo livelli predefiniti (3 / 5 / 10 / illimitato). Per assegnare a un cliente un numero **esatto** di posti nel team — 2, 7, 15, qualsiasi numero — imposta invece `usage_limits.team_seats_limit`. Questa impostazione prevale sui valori predefiniti e la piattaforma la applica a ogni invito, aggiunta diretta e accettazione di invito: una volta raggiunto il limite, ulteriori inviti vengono rifiutati lato server.

- Un numero intero positivo rappresenta il limite esatto.
- `0` significa che i membri del team **non sono inclusi** — il cliente non può invitare nessuno.
- `-1` significa illimitato.
- `null` cancella il limite personalizzato e ripristina qualsiasi valore predefinito `team_seats_*` presente nell'elenco delle funzionalità.

Ridurre il limite non rimuove mai i membri del team esistenti; impedisce solo l'aggiunta di nuovi membri.

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

Puoi impostarlo anche al momento della creazione: `POST /v1/subaccounts` accetta `usage_limits.team_seats_limit` con la stessa semantica. Per leggere il valore corrente, recupera il sub-account con `GET /v1/subaccounts?email=...` e controlla `usage_limits.team_seats_limit` (assente/`null` = decidono le impostazioni predefinite). Lo stesso endpoint aggiorna anche `credits`, `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months`, `rollover_expiry_days` e `byok_monthly_limit_usd` — invia solo le chiavi che desideri modificare.

**In che modo questo interagisce con i limiti di posti del piano SaaS.** I tuoi piani SaaS possono avere una propria dotazione di posti (impostata nell'editor del piano — vedi [Posti del team in un piano](agency-accounts.md#step-3--set-up-pricing-tiers)), che viene applicata automaticamente quando un cliente si abbona. Un limite impostato tramite questo endpoint viene conteggiato come una concessione **manuale**: l'acquisto di un piano lo sostituisce con la dotazione di posti del piano stesso (quell'acquisto è una scelta esplicita del piano), ma i **rinnovi** mensili automatici **non sovrascrivono mai un limite manuale**; pertanto, un'eccezione una tantum concessa a un cliente rimane valida per tutto il suo ciclo di fatturazione. L'eliminazione del limite manuale con `null` restituisce il controllo del campo al piano al momento del rinnovo successivo.

**Limita ciò che un cliente trasporta tra i rinnovi.** Altre due chiavi `usage_limits` si trovano accanto a `roll_over_to_next_month`. Entrambe sono accettate anche da `POST /v1/subaccounts` al momento della creazione, e `null` le cancella entrambe.

| Chiave | Cosa fa |
|---|---|
| `rollover_cap_months` | Mesi di credito che il cliente può conservare. Un numero da 0 a 120, sono consentite frazioni (`0.5` = mezzo mese). A ogni rinnovo, il saldo inutilizzato viene ridotto al massimo a questo numero di volte il credito concesso dal rinnovo, prima che vengano aggiunti i nuovi crediti; `0` non trasporta nulla. |
| `rollover_expiry_days` | Un numero intero di giorni, da 1 a 3650. I crediti rimasti inutilizzati per così tanto tempo vengono eliminati al primo rinnovo dopo aver raggiunto tale età. La spesa viene sempre detratta dai crediti più vecchi, quindi un cliente che spende il proprio credito ogni mese non ne perde mai nessuno. |

Se non impostati, entrambi tornano al piano del cliente; un valore inviato qui prevale su quello del piano. Solo i crediti ricorrenti (il credito mensile e i crediti del piano) ne sono soggetti: le ricariche, i rinnovi automatici e le aggiunte una tantum non vengono mai limitati o fatti scadere. Ogni riduzione viene scritta nella cronologia dei crediti del cliente come **Rollover Cap Credit Adjustment** o **Expired Credits Credit Adjustment** e non viene mai conteggiata come utilizzo. Gli equivalenti a livello di piano sono `rollover_cap_months` e `rollover_expiry_days` su un livello di prezzo — vedi [I campi su un livello](#the-fields-on-a-tier) e [Limitare ciò che viene trasportato](sub-accounts.md#capping-what-rolls-over).

***

## Imposta prezzi e policy IA per cliente

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

Altri otto interruttori per cliente, insieme a `/limits`, `/features` e `/menu-visibility` sopra. Ognuno prende l'uid del sub-account nell'URL (nessun parametro `sub_account_id` nel corpo/query — l'obiettivo è già nominato nel percorso) ed è limitato allo stesso modo: la tua chiave di agenzia, e il sub-account deve appartenere alla tua agenzia.

**Quali modelli IA può utilizzare un cliente**

```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 }` abilita (o disabilita) il cliente al livello Max AI — la nostra infrastruttura al prezzo di listino della piattaforma. Attivare questa opzione per un cliente BYOK cambia il costo della sua IA da "gratuito con la mia chiave" a "addebitato sul mio pool di crediti", quindi è una decisione deliberata per singolo cliente piuttosto che un'impostazione predefinita per l'intera agenzia.

Per limitare i livelli da cui le campagne e gli Agenti di un cliente possono scegliere (invece di limitare solo Max), usa `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` è un array tratto da `standard`, `economy`, `max`, `mini` — SOSTITUISCE l'elenco consentito del cliente. Invia `null` (o `[]`) per cancellare la restrizione e consentire loro di scegliere qualsiasi livello. Questo è importante perché un sub-account che sceglie il proprio livello IA spende dal **tuo** pool di crediti, quindi è la leva per stabilire quali modelli un cliente rivenditore può utilizzare a tue spese.

**Una risposta di attesa mentre il cliente è senza crediti**

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

Quando il saldo del cliente (o il tuo pool) è vuoto, l'IA non può rispondere e il contatto non riceve nulla. Con `enabled: true`, ogni contatto che scrive durante l'interruzione riceve `message` una volta (massimo 500 caratteri, inviati così come sono su ogni canale), e l'IA risponde a quelle conversazioni concretamente una volta che i crediti sono tornati. `enabled: false` conserva il testo salvato per dopo; `enabled: false` senza `message` rimuove l'impostazione. Stesso interruttore di **Risposta di attesa quando si è senza crediti** nel modale Modifica del sub-account — vedi [Una risposta di attesa mentre un cliente è senza crediti](sub-accounts.md#a-holding-reply-while-a-client-is-out-of-credits).

**Cosa paga un cliente per azione IA e il ricarico sulla tariffa WhatsApp**

Due modi per impostare la tua tariffa rivolta al cliente, dal più semplice al più granulare:

```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` è il prezzo in crediti che il saldo PROPRIO del sub-account consuma per ogni azione IA del modello Max — il tuo ricarico rivolto al cliente oltre a ciò che il tuo pool paga effettivamente. `null` ripristina l'override al prezzo di listino della piattaforma. La tariffa deve essere almeno pari al costo di un'azione Max per il tuo pool (così non puoi mai far pagare un cliente meno del tuo costo) e non superiore a 10 crediti; una richiesta al di fuori di tale finestra viene rifiutata con il limite minimo calcolato nel messaggio di errore.

Per prezzi basati sul tipo di azione invece di una tariffa fissa Max, usa `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` è una FUSIONE sulla mappa esistente del cliente — una chiave che non menzioni viene lasciata com'era, e `null` reimposta quella chiave al suo valore predefinito. Le chiavi riconosciute:

| Chiave | Prezzi |
|---|---|
| `AI_MESSAGE` | Una risposta IA |
| `AI_TOOL_USE` | Una chiamata a uno strumento IA |
| `EVALUATION_CALL` | Un passaggio di valutazione chat |
| `INTERRUPTION_HANDLING` | Gestione di un'interruzione durante la risposta |
| `CONTACT_TAG` | Un tag contatto assegnato dall'IA |
| `CHAT_SUMMARY` | Un riepilogo chat |
| `wa_carrier_multiplier` | Un moltiplicatore di ricarico applicato a ogni tariffa WhatsApp non IA che il cliente paga: affitto mensile del numero, tariffe di consegna su corsia gestita e costi di transito dei template Meta/Twilio. |

Le tariffe per azione devono essere un numero superiore a 0 e fino a 10; `wa_carrier_multiplier` deve essere almeno `1` (nessuno sconto al di sotto del costo) e fino a 10. L'invio di una chiave non riconosciuta, o di un valore fuori intervallo, rifiuta l'INTERA richiesta e indica ogni chiave non valida, in modo che un errore di battitura non possa mai salvare silenziosamente un prezzo che non viene effettivamente applicato.

Se sei un membro del [Champions Circle](https://skool.com/dm-champions), `insider-rate` trasmette la tua tariffa scontata del 20% per Max/Lead Finder a un solo cliente invece di applicarla a tutta l'agenzia:

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

Attivarlo richiede che il tuo account agenzia detenga effettivamente l'iscrizione al Circle; disattivarlo non lo richiede mai, quindi un membro scaduto può sempre riportare un cliente al livello precedente.

**Blocca sezioni del playbook di un cliente**

```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` è un array tratto da `instructions`, `goal`, `rules`, `personality`, `conclude_unless` — SOSTITUISCE l'elenco bloccato del cliente. Una sezione bloccata viene rifiutata lato server se il SUB-ACCOUNT stesso tenta di modificarla (direttamente o tramite chiave API), mentre tu (tramite `sub_account_id`) e la vista amministratore della dashboard del cliente potete ancora modificare tutto. Invia `null` (o `[]`) per sbloccare tutto. Utile per i clienti "done-for-you" dove sei tu a possedere il playbook e vieni giudicato in base al risultato.

**Imposta le preferenze di notifica di un cliente per suo conto**

```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` sostituisce l'intero set di preferenze di notifica del cliente (non è un merge per chiave: invia ogni categoria che vuoi mantenere, in modo corrispondente a come la salva la pagina Impostazioni del sub-account). Ogni categoria sotto `settings` accetta `enabled` (booleano) e fino a tre `channels` da `email`, `in_app`, `webhook`. Invia `null` per ripristinare le impostazioni predefinite della piattaforma.

Tutti e sette gli endpoint rispondono `{ "success": true, "data": { "subAccountUid": "...", ...the field(s) you set... } }` e vengono registrati nel log di controllo con il valore prima/dopo. Errori comuni: `403` se il tuo account non è Agency/Dev o il sub-account non è di tua gestione, `400` se non è un sub-account di agenzia o un valore è fuori intervallo.

***

## Sospendi un cliente che ha interrotto il proprio abbonamento

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

Quando un cliente sospende il proprio abbonamento con te, metti in pausa il suo account invece di eliminarlo: tutto ciò che invia si interrompe immediatamente — messaggi in uscita, broadcast, risposte AI su ogni canale — e quando accede, visualizza un blocco a schermo intero **Account sospeso** (con il tuo messaggio opzionale) invece dell'app. Nulla viene eliminato o disconnesso: agenti, campagne, canali collegati, contatti e cronologia chat rimangono esattamente come sono, quindi la riattivazione riporta il cliente esattamente dove aveva interrotto — nessuna configurazione da rifare.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `message` | No | Mostrato al cliente sulla schermata di blocco. Lascialo vuoto per il testo predefinito. |
| `reason` | No | Nota interna all'agenzia archiviata con la sospensione e nel registro di controllo — mai mostrata al cliente. |

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

**Risposta**

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

Quando il cliente ritorna, `POST /v1/subaccounts/SUB_ACCOUNT_UID/unpause` (nessun corpo) rimuove il blocco — l'invio e le risposte AI riprendono immediatamente.

Cose da sapere:

- **È lo stesso stato dell'interruttore Blocco totale della dashboard** ([Blocco / Sospensione di un sub-account](sub-accounts.md#blocking-pausing-a-sub-account)) — un cliente messo in pausa tramite API appare come bloccato nella dashboard e viceversa, e la riattivazione rimuove un blocco applicato da entrambi i lati. Lo stato attuale è leggibile dal campo `agency_block` su `GET /v1/subaccounts` (`level` di `"none"`, `"soft_blocked"` o `"hard_blocked"`).
- **Entrambe le chiamate sono idempotenti.** Mettere in pausa un cliente già in pausa aggiorna solo il messaggio, il motivo e il timestamp; riattivare un cliente attivo non cambia nulla.
- **Il cliente non riceve email automaticamente** — molte agenzie utilizzano il white-label, quindi spetta a te informare il cliente.
- **La tua fatturazione DM Champ rimane invariata.** Mettere in pausa un cliente influisce solo sul tuo rapporto con lui.
- **Anche gli assistenti AI possono farlo**: il [server MCP](../integrations/connect-ai-clients.md) espone questi endpoint come strumenti `pause_subaccount` e `unpause_subaccount`.

***

## Assegna o detrai crediti direttamente

`POST /v1/subaccounts/credits`

Aggiunge o rimuove un importo esatto di crediti dal saldo di un sub-account — l'equivalente API della regolazione manuale dei crediti della dashboard. Si tratta di una variazione del saldo una tantum, distinta dalle impostazioni ricorrenti `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months` e `rollover_expiry_days` su [`PUT /v1/subaccounts/{subAccountUid}/limits`](#set-an-exact-team-member-limit-for-a-client).

Questo è l'unico endpoint in questa pagina che identifica il sub-account tramite **email** anziché `sub_account_id`.

| Campo | Obbligatorio | Descrizione |
|---|---|---|
| `email` | Sì | L'email del sub-account, così come esiste sotto la tua agenzia. |
| `amount` | Sì | Numero di crediti diverso da zero. Positivo aggiunge, negativo detrae. |
| `description` | No | Mostrato accanto alla rettifica nella cronologia crediti del cliente. Per impostazione predefinita è una riga generica "Rettificato dall'agenzia tramite 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" }'
```

**Risposta**

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

Un `amount` negativo che porterebbe il saldo sotto lo zero viene rifiutato con `400`, indicando il saldo disponibile e quanto hai tentato di detrarre. Se il cliente utilizza la propria fatturazione Stripe (modalità rivenditore), un importo aggiunto conta anche come crediti acquistati, quindi sopravvive al successivo ripristino mensile allo stesso modo di una vera ricarica; su un cliente con allocazione standard, viene trattato come parte del suo credito ricorrente. In entrambi i casi si tratta di un'aggiunta una tantum, quindi un limite di rollover o una scadenza impostata sull'account (o sul suo piano) non li riduce mai — solo il credito ricorrente e i crediti del piano sono soggetti a tali limiti.

***

## Leggi le conversazioni di un sub-account

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

Ti consente di creare una vista di monitoraggio o di supporto delle conversazioni di un cliente senza accedere al suo account. Per prima cosa elenca i suoi contatti con un'anteprima dell'ultimo messaggio, quindi leggi l'intera cronologia dei messaggi di un contatto.

**Elenca contatti**

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

| Parametro di query | Obbligatorio | Descrizione |
|---|---|---|
| `pageSize` | No | Contatti per pagina. Predefinito 25, massimo 50. |
| `lastActivityAt` | No | Cursore di paginazione: passa il `lastActivityAt` della pagina precedente per continuare. |
| `searchQuery` | No | Filtra per nome del contatto o numero di telefono. |

**Risposta**

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

I contatti sono ordinati in base all'attività più recente. Continua la paginazione con `lastActivityAt` finché `hasMore` è `true`.

**Leggi i messaggi di un contatto**

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

| Parametro di query | Obbligatorio | Descrizione |
|---|---|---|
| `pageSize` | No | Messaggi per pagina. Predefinito 30, massimo 100. |
| `beforeTimestamp` | No | Cursore di paginazione: recupera i messaggi precedenti a questo timestamp ISO. |

**Risposta**

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

I messaggi vengono restituiti dal più recente al meno recente; scorri la cronologia all'indietro con `beforeTimestamp`.

***

## Leggi l'utilizzo dei crediti e lo stato delle campagne per tutto il tuo portafoglio

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

Due riepiloghi in stile dashboard su ogni sotto-account che gestisci, per creare i tuoi report di agenzia invece di fare clic su ogni cliente uno alla volta.

**Utilizzo dei crediti**

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

| Parametro di query | Obbligatorio | Descrizione |
|---|---|---|
| `from` / `to` | Sì | Intervallo di date ISO. |
| `subAccountId` | No | Ometti per un riepilogo a livello di agenzia, una riga per sotto-account. Includi per passare alla modalità dettagliata: il riepilogo di quel sotto-account più i suoi record di utilizzo grezzi e impaginati. |
| `limitCount` | No | Solo modalità dettagliata. Predefinito 500, massimo 2000. |
| `startAfterTimestamp` | No | Solo modalità dettagliata: cursore di paginazione. |

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

Passa `subAccountId` e la stessa risposta conterrà anche `records`: addebiti individuali con `amount`, `reason`, `campaignName`, `contactName` e `timestamp`. Un cliente che spende utilizzando la propria chiave BYOK anziché i tuoi crediti avrà i dati su costi/token oscurati (`costsRedacted: true`): si tratta di telemetria sui costi della piattaforma, non di informazioni da mostrare a un rivenditore.

**Stato della campagna**

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

| Parametro di query | Obbligatorio | Descrizione |
|---|---|---|
| `pageSize` | No | Sotto-account per pagina. Predefinito 10, massimo 50. |
| `lastDocumentId` | No | Cursore di paginazione. |
| `searchQuery` | No | Filtra per nome o email del sotto-account. |

```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` segnala i sotto-account che meritano attenzione — ad esempio, una campagna in pausa o una a cui non è stato instradato alcun canale. Utilizzalo per creare una dashboard di controllo dello stato per l'intero portafoglio, invece di aprire ogni cliente per notare una campagna in stallo.

> Per la messaggistica basata su serie temporali e l'attività di credito per ogni cliente (una serie pronta per i grafici piuttosto che un'istantanea puntuale), consulta `GET /analytics/agency-rollup` nella guida all'API di Analytics.

***

## Distribuisci un account cliente già configurato

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

Uno [snapshot](snapshots.md) è un modello riutilizzabile: uno o più agenti IA insieme alla loro base di conoscenza, strumenti e media, acquisiti dal tuo account. Due endpoint lo inseriscono nel tuo flusso di provisioning.

**Automatico: ogni nuovo cliente lo riceve di default.** Imposta uno snapshot come predefinito una volta sola e ogni account creato da quel momento in poi lo avrà già installato. Questo copre gli account creati tramite `POST /v1/subaccounts`, gli account creati nella dashboard e quelli creati automaticamente quando un cliente paga tramite il tuo link di checkout.

Per prima cosa, trova l'id dello snapshot:

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

Quindi impostalo come predefinito:

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

L'integrazione finisce qui. Invia `{"snapshot_id": null}` per disattivarlo di nuovo. Puoi fare la stessa cosa dalla dashboard cliccando sulla stella nella pagina **Snapshot**.

Per leggere ciò che è attualmente contrassegnato come preferito (ad esempio, prima che uno script di provisioning decida se impostarne uno), `GET /v1/snapshots/default` restituisce `{ "success": true, "data": { "default_snapshot_id": "SNAPSHOT_ID" } }` — `null` quando nulla è contrassegnato. `GET /v1/snapshots` (utilizzato per trovare l'id sopra) restituisce lo stesso `default_snapshot_id` insieme all'array completo `snapshots`, quindi la maggior parte delle integrazioni necessita solo di questa chiamata. I campi completi dell'oggetto snapshot si trovano nella guida [Snapshots](snapshots.md).

**Su richiesta: installazione in un singolo account.** Utile per l'onboarding di un cliente esistente o per fornire a un cliente un secondo modello in un secondo momento.

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

Ometti `sub_account_id` e verrà installato nel tuo account agenzia. Come per gli endpoint `/subaccounts`, questi specificano l'account di destinazione nel percorso o nel corpo della richiesta, anziché tramite il parametro `sub_account_id` ambientale.

Cose da sapere prima di iniziare a sviluppare:

- **Gli agenti installati iniziano in pausa.** Collega prima i canali del cliente, poi attiva l'agente. Questo vale sia per il percorso automatico che per quello su richiesta.
- **Il provisioning non fallisce mai a causa di uno snapshot.** Se l'installazione non può essere completata, l'account cliente viene comunque creato e rimane utilizzabile; arriverà semplicemente vuoto e potrai applicare lo snapshot in un secondo momento.
- **Canali, calendari e connessioni OAuth non vengono mai copiati.** Ogni account collega i propri. Gli strumenti che utilizzano una semplice chiave API continuano a funzionare immediatamente.
- **L'applicazione doppia crea una seconda copia.** Nulla viene sovrascritto.

***

## Crea il template stesso tramite API

`POST /v1/snapshots` · agenti, funzioni personalizzate e media tramite API

La sezione precedente distribuisce un'istantanea creata da qualcuno nella dashboard. Anche la parte di creazione è esposta, quindi l'intero ciclo — assemblare la configurazione principale una volta, acquisirla, consegnarla a ogni client — può essere eseguito dal codice.

I componenti, nell'ordine in cui uno script di provisioning li utilizza:

1. **Crea le tue funzioni personalizzate.** `POST /v1/custom-functions` ne crea una; `GET /v1/custom-functions` elenca quelle che hai, e `GET`, `PUT` e `DELETE` su `/v1/custom-functions/{customFunctionId}` leggono, aggiornano e rimuovono una funzione. `POST /v1/custom-functions/test` esegue una simulazione di una definizione prima di salvarla.
2. **Crea e configura l'agente.** `POST /v1/agents` lo crea, `PUT /v1/agents/{agentId}` lo aggiorna, e `PATCH /v1/agents/{agentId}/active` con `{ "active": false }` lo mantiene in pausa mentre lavori (la stessa chiamata con `true` lo rende attivo). `GET /v1/agents` li elenca.
3. **Assegna le funzionalità all'agente.** `POST /v1/agents/{agentId}/custom-functions` con `{ "custom_function_id": "..." }` collega una funzione all'agente; il corrispondente `DELETE /v1/agents/{agentId}/custom-functions/{customFunctionId}` la scollega.
4. **Riempi la libreria multimediale.** `POST /v1/agents/{agentId}/media-library` carica un elemento (JSON con `base64Data`, `mimeType`, `title`, `description`); `GET` elenca gli elementi dell'agente, e `PATCH`/`DELETE` su `/{itemId}` ne aggiornano o rimuovono uno.
5. **Acquisiscilo come istantanea.**

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

Da lì si passa alla sezione precedente: impostalo come predefinito in modo che ogni nuovo client ne sia dotato fin dall'inizio, oppure applicalo su richiesta. La gestione vive parallelamente: `PATCH /v1/snapshots/{snapshotId}` con `{ "name": "..." }` ne rinomina uno, `DELETE /v1/snapshots/{snapshotId}` ne elimina uno (e rimuove l'impostazione di predefinito se lo era), e `GET /v1/snapshots/apply-targets` elenca ogni account in cui potresti eseguire l'installazione.

Gli endpoint per agenti, funzioni personalizzate e media accettano tutti `sub_account_id`, quindi le stesse chiamate possono anche gestire un agente direttamente all'interno dell'account di un client. Le chiamate alle istantanee agiscono sempre sul tuo account di agenzia: il template rimane con te. Gli schemi completi di richiesta e risposta per tutti questi elementi si trovano nel [Riferimento API](../api/reference.md).

***

## Gestisci i tuoi livelli di prezzo tramite API

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

I piani che vendi in **Modalità SaaS → Livelli di prezzo** possono essere letti e modificati dal codice, in modo che il tuo pannello di amministrazione o script di provisioning possa aggiungere un piano, modificare un prezzo o fornire un link di checkout senza che nessuno debba aprire la dashboard. Autenticati con la tua chiave API di agenzia come per ogni altra chiamata in questa pagina; questi endpoint sono a livello di agenzia, quindi non richiedono alcun `sub_account_id`. Ogni scrittura esegue la stessa convalida e la stessa sincronizzazione di prodotti e prezzi di Stripe del salvataggio nella dashboard, quindi un piano creato qui è indistinguibile da uno configurato manualmente.

### Elenca i tuoi livelli

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

**Risposta**

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

Ogni livello viene restituito con il suo **`tierIndex`** — la sua posizione nell'elenco dei Piani, che è il modo in cui le altre tre chiamate vi fanno riferimento — e un **`checkout_url`** pronto per essere condiviso, lo stesso link fornito dalla scheda **Pagamenti**, che punta già al [dominio white label](white-labeling.md) su cui viene venduto quel piano.

### Aggiungi un livello

Il corpo è un oggetto di livello; viene aggiunto alla fine del tuo elenco.

```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 risposta contiene il livello creato, incluso l' `tierIndex` su cui è stato inserito e il suo `checkout_url`.

### Modifica un livello

Invia solo i campi che desideri modificare; tutto il resto del piano rimarrà invariato.

```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 campo che l'API non riconosce viene rifiutato anziché ignorato, e l'errore lo indica — così un errore di battitura non può mai scrivere silenziosamente un'impostazione che sembra attiva ma non fa nulla. La modifica del prezzo, dei crediti, della valuta o della cadenza di fatturazione crea un nuovo prezzo nel tuo Stripe; i clienti che si sono già abbonati rimangono su ciò per cui si sono iscritti.

### Elimina un livello

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

Stessa regola della dashboard: un piano che ha ancora abbonati attivi non può essere eliminato. La richiesta viene rifiutata, indicandoti quanti abbonati sono presenti su di esso — annullali o migrali prima. Un'eliminazione riuscita risponde con i tuoi livelli rimanenti, già rinumerati.

### I campi di un livello

| Campo | Cos'è |
|---|---|
| `label` / `description` | Il nome del piano e la riga opzionale mostrata sulla tua pagina di checkout. |
| `credits` | Crediti che il cliente ottiene **al mese** su un piano mensile o annuale, e **per periodo di fatturazione** su uno settimanale. |
| `price_cents` | Prezzo per intervallo di fatturazione, nella più piccola unità di valuta (`2900` = $29.00). Su un piano annuale, questo è il prezzo dell'intero anno. |
| `currency` | Codice ISO in minuscolo — `usd`, `eur`, `gbp` e così via. |
| `billing_interval` / `billing_interval_count` | `month` (predefinito), `year`, o `week` con un conteggio da 1 a 52 per "ogni N settimane". |
| `trial_days` | Durata della prova gratuita, da 0 a 90. `0` (o lasciarlo vuoto) significa nessuna prova. |
| `trial_credits` | Crediti con cui il cliente inizia la prova. Predefinito al `credits` del piano. |
| `trial_card_required` | `false` consente al cliente di iniziare la prova senza inserire una carta. Predefinito a `true`. |
| `trial_hard_expiry` | `true` restituisce i crediti di prova inutilizzati al tuo pool e blocca l'account del cliente quando una prova termina senza un upgrade. Predefinito a `false` — vedi [Scadenza rigida dopo la prova](agency-accounts.md#step-3--set-up-pricing-tiers). |
| `rollover_cap_months` | Mesi di credito che i clienti su questo piano possono trasportare tra i rinnovi — un numero da 0 a 120, sono consentite frazioni. `0` non trasporta nulla; `null` (predefinito) significa nessun limite. Vedi [Limitare ciò che viene trasportato](sub-accounts.md#capping-what-rolls-over). |
| `rollover_expiry_days` | Giorni dopo i quali i crediti inutilizzati vengono eliminati al rinnovo successivo — un numero intero da 1 a 3650. `null` (predefinito) significa che non scadono mai. |
| `features` / `feature_settings` | Cosa ottengono i clienti su questo piano — gli stessi ID funzionalità di [Scegli quali tipi di canale un cliente può connettere](#choose-which-channel-types-a-client-can-connect). |
| `team_seats_limit` | Posti team concessi dal piano: un numero esatto, `0` per nessuno, `-1` per illimitati. |
| `white_label_config` | Su quale dei tuoi [domini white label](white-labeling.md#up-to-three-white-labels) viene venduto il piano. |

I campi della prova hanno significato solo in un piano che prevede una prova: salva un livello con `trial_days: 0` e verranno eliminati. Gli ID prodotto e prezzo Stripe del piano sono gestiti per te e non possono essere impostati manualmente.

Tre cose da fare correttamente:

- **Gli indici dei livelli sono posizioni, non ID permanenti.** L'eliminazione di un piano sposta in avanti ogni piano successivo, quindi recupera nuovamente l'elenco dopo qualsiasi modifica e copia di nuovo i link di checkout che hai pubblicato, esattamente come faresti dopo aver eliminato un piano nella dashboard.
- **La modalità SaaS deve essere configurata per prima.** Questi endpoint richiedono un account agenzia con white labeling e una chiave Stripe già salvata; senza di essa, non esiste un account Stripe su cui far risiedere il prodotto e il prezzo del piano.
- **Il limite è di venti piani**, lo stesso della dashboard. Il campo `max_tiers` nella risposta dell'elenco ti indica il limite attuale.

Gli schemi completi di richiesta e risposta sono disponibili nel [Riferimento API](../api/reference.md), sotto **Agenzia**.

***

## Imposta il tuo prezzo per credito tramite API

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

Il prezzo che i clienti pagano per le ricariche ad-hoc (**Modalità SaaS → Prezzo per credito**) può essere letto e modificato anche tramite codice. Questa funzione è pensata per i casi in cui il prezzo deve variare autonomamente: un'agenzia che vende crediti in una valuta ma addebita in un'altra può consentire a un processo pianificato di aggiornare il prezzo al variare del tasso di cambio, invece di doverlo modificare manualmente ogni settimana.

### Leggi il prezzo attuale

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

**Risposta**

```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` è il prezzo più basso consentito dalla piattaforma in quella valuta, quindi un processo può verificare un nuovo prezzo prima di inviarlo. Tutti e tre i valori sono `null` finché non viene impostato un prezzo.

### Modificalo

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

Invia solo ciò che desideri modificare. `price_per_credit_cents` è il prezzo nella valuta più piccola (`130` = R$1,30); `currency` è un codice ISO in minuscolo; `note` è una riga facoltativa di massimo 200 caratteri mostrata ai clienti direttamente sotto il prezzo per credito nella loro pagina di fatturazione: utile per un prezzo di riferimento in un'altra valuta, come *"0,25 USD per credito al nostro tasso di riferimento"*. Invia `"note": ""` per rimuoverlo. La risposta ha la stessa forma della lettura precedente, quindi un processo può confrontare e saltare la scrittura quando non è cambiato nulla.

Si applicano le stesse regole della dashboard: il prezzo non può scendere al di sotto del minimo della piattaforma per quella valuta e l'account deve avere il white labeling. A differenza degli endpoint dei livelli di prezzo, non è richiesta alcuna chiave Stripe per leggere o modificare questo valore.

### Assegna a un processo una chiave che non può fare altro

Inserire la chiave completa della tua agenzia in uno scheduler fornisce più accessi di quanto un aggiornamento di prezzo richieda. Crea invece una **chiave con ambito limitato** (scoped key) circoscritta all'area **Prezzo Credito Agenzia**: tale chiave può leggere e modificare il prezzo per credito e nient'altro; non può accedere a sotto-account, piani, crediti o alla tua connessione 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 risposta contiene la nuova chiave in `api_key` **una sola volta**; non verrà mai più mostrata, quindi salvala immediatamente. Imposta `"read_only": true` per una chiave che deve solo leggere il prezzo e aggiungi `"expires_at"` (una data ISO) se vuoi che smetta di funzionare automaticamente. Solo la chiave del proprietario dell'account può creare chiavi con ambito limitato; elencale o revocale con `GET /v1/api-keys` e `DELETE /v1/api-keys/{id}`.

***

## Consenti a un sotto-account di leggere i tuoi prezzi

`GET /v1/subaccounts/agency-pricing`

Ogni altro endpoint in questa pagina viene chiamato con la **tua chiave di agenzia**, puntando facoltativamente a un cliente tramite `sub_account_id`. Questo è l'opposto: viene chiamato con la **chiave API del sotto-account stesso**, senza `sub_account_id`, in modo che la pagina di ricarica del cliente (o un'integrazione che costruisci per loro) possa visualizzare ciò che addebiti loro senza mai vedere il tuo account di agenzia.

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

**Risposta**

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

Questo rispecchia esattamente ciò che [`GET /v1/agency/pricing-tiers`](#list-your-tiers) e [`GET /v1/agency/credit-price`](#read-the-current-price) restituiscono per te come agenzia, meno tutto ciò che il cliente non ha bisogno di vedere (ID Stripe, `max_tiers`, ecc.). Funziona solo per un account che è effettivamente un sotto-account con un'agenzia collegata — chiamarlo dal tuo account di agenzia restituisce un errore di autorizzazione.

***

## Cose da tenere a mente

- **Usa la tua chiave di agenzia.** Autentica ogni chiamata con la chiave API del tuo account di agenzia, non quella del sub-account. Il parametro `sub_account_id` è ciò che reindirizza l'azione.
- **I crediti provengono dal sub-account.** Gli acquisti e gli addebiti ricorrenti incidono sul saldo crediti del sub-account di destinazione, non sul tuo.
- **Un `404` significa "non è il tuo sub-account".** Ricontrolla l'id e assicurati che l'account sia uno di quelli che gestisci.
- **Il parametro è facoltativo ovunque sia accettato.** Omettilo e lo stesso endpoint agirà sul tuo account di agenzia, così potrai riutilizzare un'unica integrazione per entrambi.

***

## Correlati

- [Accesso API](../integrations/api-access.md) — autenticazione, URL di base, errori, limiti di frequenza.
- [Sotto-account](sub-accounts.md) — elenca e gestisci gli account che puoi gestire.
- [Ricarica automatica sotto-account](sub-account-auto-recharge.md) — concedi crediti a un sotto-account tramite webhook + API.
- [API Campagne](../api/campaigns.md) — crea, aggiorna e copia campagne, incluso il riferimento completo ai campi.
- [API Connessione Canale](../api/channels.md) — connetti i canali di un cliente e instradali verso una campagna.
- Guida all'API di Analytics (nella sezione API) — il riepilogo dei sotto-account dell'agenzia e ogni altro endpoint di reportistica.
- [Snapshots](snapshots.md) — cosa cattura uno snapshot e come costruirne uno nella dashboard.
