
# API toimistoille

Toimistona voit käyttää samaa REST API -rajapintaa kuin asiakkaasi, mutta kohdistaa yksittäiset pyynnöt hallinnoimiesi **alitilien** sijaan omaan tiliisi. Tämän avulla voit rakentaa työkaluja, jotka hoitavat asiakkaan käyttöönoton alusta loppuun – luovat kampanjoita, kouluttavat tekoälyn tietokannan pohjalta, tuovat yhteystiedot, yhdistävät viestintäkanavat ja ostavat puhelinnumeroita – ilman, että sinun tarvitsee kirjautua manuaalisesti jokaiseen alitiliin.

Tämä sivu käsittelee vain toimistokohtaista toimintaa: miten toimitaan alatilin puolesta `sub_account_id`-parametrilla. Perusasioiden osalta (avaimen luominen, tunnistautuminen, perus-URL, virhemuoto, nopeusrajoitukset) aloita [API-käyttöoikeus](../integrations/api-access.md) -oppaasta. Kaikki siellä mainittu pätee myös täällä – tunnistaudut **toimistotilisi** API-avaimella.

::: note
**Huomautus:** Tämä sivu on tekninen. Jos et ole kehittäjä, jaa se integraatiotasi rakentavan henkilön kanssa.
:::


***

## Miten "toisen puolesta toimiminen" toimii

Oletusarvoisesti jokainen API-pyyntö kohdistuu tiliin, joka omistaa API-avaimen – eli toimistotiliisi. Jos haluat toimia hallinnoitavan asiakastilin puolesta, lisää pyyntöön valinnainen `sub_account_id`-parametri ja aseta se kyseisen asiakkaan tilin tunnukseksi.

- **Jätä `sub_account_id` pois** → pyyntö kohdistuu omaan toimistotiliisi.
- **Sisällytä `sub_account_id`** → pyyntö kohdistuu kyseiseen alatiliin, mutta vasta sen jälkeen, kun alusta on vahvistanut, että alatili todella kuuluu sinulle.

Tunnistaudut aina **toimistotilisi** API-avaimella. Et tarvitse koskaan alatilin omaa avainta, etkä käsittele alatilin kirjautumistietoja.

### Mihin se lisätään

- **GET / DELETE -päätepisteet** → välitä se kyselyparametrina: `?sub_account_id=THE_SUB_ACCOUNT_ID` (yhdessä `apiKey`-parametrisi kanssa, jos käytät tunnistautumiseen kyselyä).
- **POST / PUT / PATCH -päätepisteet** → sisällytä se JSON-pyyntörunkoon muodossa `"sub_account_id": "THE_SUB_ACCOUNT_ID"`.
- **AI-assistentit** → ei konfiguroitavaa. [MCP-palvelin](../integrations/connect-ai-clients.md) käyttää samaa asetusta luku-työkaluissaan, joten yksi yhteys toimistoavaimellasi voi raportoida jokaisesta asiakkaasta: nimeä vain asiakas pyynnössäsi ("kuinka monta yhteystietoa Bella's Bistrolla on?"). Myös kirjoitustoiminnot ovat käytettävissä: jokainen päätepiste, joka hyväksyy `sub_account_id`-parametrin, on käytettävissä työkaluna, joten voit luoda, muuttaa ja lähettää viestejä asiakkaan puolesta saman yhteyden kautta.

### Alatilin tunnuksen löytäminen

`sub_account_id` on asiakastilin yksilöllinen tunniste. Voit hakea luettelon alatileistäsi ja niiden tunnisteista **SubAccounts**-rajapintojen kautta (katso [Sub-Accounts](sub-accounts.md)-opas) tai sivupalkin **Sub Accounts** -sivulta.

***

## Omistajuus tarkistetaan aina

Kun välität `sub_account_id`-parametrin, alusta tarkistaa, että tili on todellinen alatili **ja** että se kuuluu toimistollesi. Vasta sitten pyyntö suoritetaan.

Jos tunnus on tuntematon, se ei ole alatili tai se kuuluu toiselle toimistolle, pyyntö epäonnistuu ja palauttaa **`404`**-vastauksen:

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

> **Miksi 404 eikä 403?** "Kielletty"-vastaus kertoisi ulkopuoliselle, että tunnus on olemassa, mutta ei kuulu hänelle. Saman `404`-vastauksen palauttaminen sekä "ei ole olemassa"- että "ei ole sinun" -tilanteissa tarkoittaa, ettei päätepistettä voida käyttää muiden toimistojen tilitunnusten selvittämiseen. Käsittele `404`-vastausta tässä yhteydessä muodossa "tämä ei ole hallinnoimasi alatili."

***

## Missä `sub_account_id` on tuettu

`sub_account_id` hyväksytään lähes jokaisessa **resurssin** päätepisteessä – jokaisessa kutsussa, joka luo, lukee, päivittää tai poistaa tilin omia tietoja. Käytännössä voit valmistella ja suorittaa alitilin koko asetukset toimistoavaimellasi:

- **Tekoälyn asetukset** — kampanjat, agentit, UKK-osiot, tietokannan lähteet (verkkosivuston indeksointi **ja** tiedostojen lataus), tietokantaryhmät, lähetykset, mukautetut funktiot, MCP-palvelimet
- **Yhteystiedot ja CRM** — yhteystiedot (mukaan lukien tuonti), listat, tunnisteet, tehtävät, kaupat, tapaamiset, tapahtumat
- **Kanavat ja numerot** — yhdistä WhatsApp / WhatsApp Web / Telegram / Instagram & Messenger / LINE, etsi / osta / hallinnoi puhelinnumeroita, WhatsApp-mallit, kanavien reititys
- **Viestintä ja sisältö** — lähetä viestejä, chat-istunnot, chat-viennit, päivittäiset yhteenvedot
- **Asetukset ja integraatiot** — webhookit, chat-widgetin konfigurointi, white-label-konfigurointi, BYOK SMS ja muut tiliasetukset, analytiikka

Jokaisessa näistä parametri on **valinnainen** — jätä se pois, niin kutsu kohdistuu omaan toimistotiliisi, jolloin yksi integraatio palvelee molempia. Krediitit ja käyttö tulevat aina kohdetililtä: alitilin kampanjoista, viesteistä, tunnisteista ja numeroista aiheutuvat veloitukset kohdistuvat **alitilin** saldoon.

### Missä se EI päde

Muutamat päätepisteet ovat toimistotason tai itseensä viittaavia, ja ne ohittavat parametrin `sub_account_id`:

- **Itse alitilien hallinta** — SubAccounts-päätepisteet (alitilin luominen / listaaminen / päivittäminen) ja BYOK-käyttörajapäätepiste nimeävät alitilin jo omassa URL-polussaan. [Hinnoittelu- ja käytäntöpäätepisteet](#set-per-client-ai-pricing-and-policy) ja [chat-valvontapäätepisteet](#read-a-sub-accounts-conversations) noudattavat samaa kaavaa.
- **Agentin kopioiminen tilien välillä** — `POST /v1/subaccounts/agents/copy` nimeää molemmat tilit itse, ottaen kohdetilin `targetUserId`-parametrina. Katso [esimerkki](#worked-example-ship-a-template-agent-into-every-new-client) alta. (Vanhempi `POST /v1/subaccounts/campaigns/copy` toimii samalla tavalla, mutta se on vanhentunut muun [Campaigns API](../api/campaigns.md):n mukana.)
- **Krediittien säätäminen ja kaksi koko toimiston kattavaa koontia** — [`POST /v1/subaccounts/credits`](#grant-or-deduct-credits-directly) tunnistaa alitilin sen sijaan `email`-parametrilla; [`GET /v1/subaccounts/credit-usage`](#read-credit-usage-and-campaign-health-across-your-book) ja `GET /v1/subaccounts/campaign-status` raportoivat kaikista alitileistä kerralla, joten yksittäistä kohdetiliä ei ole.
- **Toimistosi oma tili** — API-avainten hallinta, toimiston käyttöä koskeva raportointi, tiimin hallinta ja [hinnoittelutasosi](#manage-your-pricing-tiers-over-the-api) vaikuttavat aina toimistotiliisi.
- **Saapuvien viestien webhookit** — päätepisteet, joihin ulkoiset järjestelmät lähettävät tietoa, on sidottu siihen tiliin, jonka tunnuksilla ne on määritetty, joten mitään ei tarvitse ohjata uudelleen.

> Aina ajan tasalla oleva, koneellisesti luettava luettelo kunkin päätepisteen hyväksymistä parametreista löytyy hallintapaneelisi API-viitteestä (**Settings → Integrations → API Key**) sekä OpenAPI-määrityksestä osoitteessa `GET /v1/docs/openapi.yaml`. Julkaisemme API-muutoksia usein – pidä niitä ensisijaisena tietolähteenä.

::: master-only
<figure><img src="../.gitbook/assets/v2-api-access-key-section.png" alt="API-avainten asetussivu, jossa on peitetty avain ja Regenerate-ohjaus"><figcaption><p>Asetukset → Integraatiot → API-avain — toimistosi avain löytyy täältä, samoin kuin linkki täydelliseen API-ohjeistukseen.</p></figcaption></figure>
:::

***

## Esimerkki: Instagramin ja Messengerin yhdistäminen alatilille

Instagramin ja Messengerin yhdistäminen on selainpohjainen prosessi. Aloitat sen API:n kautta, annat palautetun suostumus-URL-osoitteen asiakkaalle (tai avaat sen hänen puolestaan), odotat, että hän valtuuttaa yhteyden selaimessaan, ja valitset sitten yhdistettävän sivun – kaikki tämä kohdistuen hänen alatiliinsä `sub_account_id`-parametrin avulla.

### Vaihe 1 — Yhteyden aloittaminen

Kutsu yhdistämispäätepistettä asiakkaan `sub_account_id`-tunnuksella rungossa. Tässä ei lähetetä tunnistetietoja; alusta palauttaa suostumus-URL-osoitteen, joka asiakkaan on avattava selaimessa, sekä kertakäyttöisen korrelaatiotunnisteen.

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

**Vastaus:**

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

Ohjaa asiakas osoitteeseen `oauth_url` selaimessa valtuutusta varten. `state_token` korreloi tämän yrityksen ja on lyhytikäinen salaisuus – älä kirjaa sitä lokiin. Yritys vanhenee kohdassa `expires_at`; jos se vanhenee, aloita alusta.

### Vaihe 2 — Kyselyt kunnes sivut latautuvat

Kun asiakas on antanut valtuutuksen, kysy tila-päätepistettä (samalla `sub_account_id`-arvolla, tällä kertaa kyselyparametrina), kunnes yhdistettävissä olevat sivut tulevat näkyviin.

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

**Vastaus:**

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

`status`-kenttä etenee tilojen `pending` → `token_received` → `pages_loaded` → `connected` kautta. Odota `pages_loaded`-tilaa ennen sivun valitsemista. Etenemisen sijaan voi ilmetä myös kaksi päätetilan virhettä: `failed` ja `expired` (asiakas hylkäsi suostumuksen tai tilatunnisteen noin 30 minuutin ikkuna umpeutui) — `reason`-kenttä sisältyy mukaan, kun jompikumpi näistä tapahtuu. Lopeta kysely ja aloita alusta vaiheesta 1, jos näet jonkin näistä; älä odota `pending`-tilaa ikuisesti. Sivun pääsytunnisteita ei palauteta koskaan.

### Vaihe 3 — Valitse yhdistettävä sivu

Valitse yksi sivutunnuksista (page ids) vaiheesta 2. Sivun valitseminen yhdistää sekä Instagramin että Messengerin kyseiselle sivulle. Sisällytä `sub_account_id` uudelleen runkoon.

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

**Vastaus:**

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

Siinä kaikki — Instagram ja Messenger on nyt yhdistetty asiakkaan alitilille. Olet toimittanut vain `page_id`-arvon; taustalla oleva tunnistetieto ratkaistaan palvelimella, eikä se kulje integraatiosi kautta.

***

## Esimerkki: numeron ostaminen alitilille

Numeron ostaminen toimii samalla tavalla: etsi käyttämällä `sub_account_id`-arvoa kyselyssä ja osta se sitten sisällyttämällä se runkoon. Krediitit vähennetään **alitilin** saldosta, ja numero varataan alitilille.

**Haku (cURL):**

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

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

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

**Vastaus:**

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

Numero varataan `PURCHASED`-tilassa ja WhatsApp-lähettäjän rekisteröinti jatkuu taustalla. Kysy `GET /v1/phone-numbers/{phoneNumber}/status?sub_account_id=abc123def456`-tilaa, kunnes tila saavuttaa `ONLINE`-vaiheen ennen lähettämistä.

***

## Esimerkki: malliagentin toimittaminen jokaiselle uudelle asiakkaalle

Tyypillinen toimistomalli on pitää yhtä pääagenttia toimistotililläsi, säädettynä haluamallasi tavalla, josta jokainen asiakas aloittaa, ja kopioida se jokaiselle uudelle alitilille käyttöönoton yhteydessä. Tämä vaatii kolme kutsua, eikä mitään tarvitse toistaa sen jälkeen: kopio säilyttää asetuksensa, kunnes muutat niitä.

### Vaihe 1 — Kopioi agentti

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

Vastaus sisältää uuden agentin tunnisteen kohdassa `data.agent_id`. UKK-osio, tietopankki ja mediakirjasto siirtyvät mukana; lähdetilin WhatsApp-mallit, yhdistetyt sosiaalisen median julkaisut ja yhteystiedot eivät siirry. Täydellinen kenttäluettelo löytyy [AI Agents API](../api/agents.md#copy-an-agent-into-a-sub-account-agencies):sta.

Huomaa, että tämä päätepiste käyttää `targetUserId`-arvoa `sub_account_id`-arvon sijaan — se nimeää molemmat tilit itse. Alla olevat kaksi kutsua käyttävät normaalia `sub_account_id`-parametria.

### Vaihe 2 — Kytke se päälle

Kopio saapuu aina tauotettuna, joten se ei voi viestiä kenenkään kanssa ennen kuin annat siihen luvan. Tämä on myös oikea hetki lukita tekoälytaso, jota haluat asiakkaan käyttävän; se pysyy siinä, joten sitä ei tarvitse asettaa uudelleen aikataulun mukaan.

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

Jos haluat estää asiakasta muuttamasta tasoa myöhemmin, [lukitse sallitut tasot](#set-per-client-ai-pricing-and-policy) alitilillä sen sijaan, että lähettäisit arvon uudelleen.

### Vaihe 3 — Määritä asiakkaan kanavat osoittamaan kampanjaan

Kopio saapuu ilman reititystä, joten mikään ei tavoita sitä ennen kuin teet siitä vastaajan kanavilla, jotka asiakas on yhdistänyt. Yksi kutsu per kanava:

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

Tästä eteenpäin kopioitu agentti poimii automaattisesti ensimmäisen viestin tuntemattomalta yhteyshenkilöltä kyseisellä kanavalla. Katso [Ohjaa kanava agentille](../api/entry-points.md#point-a-channel-at-an-agent) muiden kanavien ja numerokohtaisen reitityksen osalta.

> **Aseta asiakkaan aikavyöhyke, kun luot alitilin.** Lähetä `time_zone_id` kohteessa `POST /v1/subaccounts`. Kampanjan aktiiviset tunnit arvioidaan alitilin omalla aikavyöhykkeellä, joten jos asiakas luodaan ilman sitä, sen aikataulu luetaan UTC-ajan mukaan — mikä muuttaa hiljaisesti sitä, milloin avustaja saa vastata.

***

## Ohita asennusohjattu toiminto asiakkaalta, jonka määrität itse

`POST /v1/subaccounts`

Oletusarvoisesti uuden alitilin omistaja ohjataan asennusohjatun toiminnon läpi ensimmäisellä kirjautumiskerralla. Jos kyseessä on asiakas, jolle teet kaiken valmiiksi — eli rakennat kampanjan ja yhdistät kanavat ennen kuin asiakas kirjautuu sisään — välitä `guided_onboarding: false`, kun luot tilin. He päätyvät suoraan hallintapaneeliin, ja **asennusohjattu toiminto** -kohta on piilotettu heidän sivupalkistaan.

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

Jätä kenttä pois (tai lähetä `true`), niin ohjattu toiminto toimii täsmälleen kuten ennenkin, joten olemassa olevia integraatioita ei tarvitse muuttaa. Jos haluat palauttaa ohjatun toiminnon asiakkaalle myöhemmin, näytä `guided_onboarding`-kohde uudelleen käyttämällä `PUT /v1/subaccounts/{subAccountUid}/menu-visibility` (alla) — valikon näkyvyys määrittää, onko ohjattu toiminto käytettävissä, `guided_onboarding` määrittää vain ensimmäisen kirjautumisen uudelleenohjauksen.

***

## Poista tehtävät, päivittäiset yhteenvedot tai mediakirjasto käytöstä asiakkaalta

`POST /v1/subaccounts`

Nämä kolme ovat käytössä jokaisella uudella asiakkaalla, ellet toisin määritä, ja ne toimivat eri tavalla kuin kaikki muut tämän oppaan ominaisuudet: ne ovat **opt-out**-tyyppisiä, eivät opt-in. Niiden jättäminen pois kohdasta `features` ei yksinään riitä, koska vanhemman integraation `features`-luettelo ei yksinkertaisesti maininnut niitä – emme voi tietää, onko kyseessä "toimisto kytki tämän pois päältä" vai "tämä luettelo on kirjoitettu ennen kuin vaihtoehto oli olemassa".

Ilmoita se siis suoraan kohdassa `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
    }
  }'
```

Jokainen avain on valinnainen; kaikki, minkä jätät pois, pysyy päällä. Kun käytössä on `tasks: false`, tekoäly lakkaa luomasta tehtäviä kyseiselle asiakkaalle, eikä "Uusi tehtävä luotu" -sähköposteja lähetetä; kun käytössä on `daily_summaries: false`, yöllistä yhteenvetoa ei koskaan luoda tai lähetetä sähköpostitse.

`feature_settings` on ainoa asia, joka kytkee nämä kolme pois päältä luontihetkellä. Niiden jättäminen pois kohdasta `features` ei tee mitään yksinään, riippumatta siitä, miltä muu luettelosi näyttää – tämä on harkittua, jotta vanhempi integraatio ei menetä kaikkia kolmea huomaamatta.

Jos haluat muuttaa jotain näistä myöhemmin, lähetä täydellinen `features`-luettelo kohtaan `PUT /v1/subaccounts/{subAccountUid}/features` – siellä ominaisuuden sisällyttäminen luetteloon kytkee sen päälle ja pois jättäminen kytkee sen pois päältä.

***

## Kirjauta asiakkaasi automaattisesti heidän alitililleen (SSO)

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

Yksi kutsu toimisto-API-avaimellasi palauttaa valmiin URL-osoitteen, joka kirjauttaa asiakkaan suoraan hänen omalle alitililleen – ei kirjautumisnäyttöä, ei salasanavaihetta, ei mitään rakennettavaa. Avaa se uudessa välilehdessä, uudelleenohjauksena tai iframe-kehyksenä omassa tuotteessasi.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `redirect` | Ei | Sovelluksen sisäinen sivu, jolle haluat asiakkaan päätyvän, esim. `"/chats"` tai `"/agents"`. Palautetaan vastauksessa muodossa `deep_link_url`. |
| `app_base_url` | Ei | Linkin hallintapaneelin isäntä. Oletusarvona on white-label-sovelluksesi verkkotunnus (tai alustan verkkotunnus, jos sinulla ei ole sellaista). On oltava `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" }'
```

**Vastaus**

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

Kuinka käyttää sitä tehokkaasti:

- **Yksi hyppy.** `url` avaaminen kirjauttaa asiakkaan sisään ja ohjaa hänet suoraan hallintapaneelin `redirect`-sivulle – ei kirjautumisnäyttöä tai välisivuja. `deep_link_url` nimeää saman kohteen integraattoreille, jotka haluavat ohjata käyttäjän tiettyyn näkymään kirjautumisen jälkeen; kun istunto on olemassa, mikä tahansa hallintapaneelin polku toimii kyseisessä selainkontekstissa.
- **Luo pyydettäessä, avaa välittömästi.** Linkki sisältää kirjautumistunnisteen ja se vanhenee noin tunnin kuluttua. Pyydä se palvelinpuolella sillä hetkellä, kun asiakas klikkaa linkkiä, äläkä koskaan tallenna tai lähetä sitä sähköpostitse.
- Kirjautumistunniste kulkee URL-fragmentissa (`#…`), jota selaimet eivät koskaan lähetä palvelimille, ja se poistetaan osoiteriviltä heti, kun se on käytetty.
- **Vain omat alitilit.** Päätepiste hylkää kaikki tilit, jotka eivät kuulu toimistollesi.
- Vanhentunut linkki näyttää selkeän virheilmoituksen ja uudelleenyrityspolun – luo uusi linkki tarvittaessa.

***

## Piilota navigointikohteita alitililtä

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

Hallitsee, mitä sivupalkin ja asetusten kohteita alitili näkee – hyödyllinen, kun upotat kojelaudan ja haluat näyttää vain ne osat, joita tuotteesi ei vielä kata. Kaikki listaamattomat pysyvät näkyvissä; lähetä `null` koko `menuVisibility`-arvona palauttaaksesi kaiken näkyviin. Kohteen piilottaminen piilottaa valikkomerkinnän – yhdistä se alitilille myöntämiisi ominaisuuksiin tiukkaa rajoittamista varten.

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

**Vastaus**

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

`side_nav` hyväksyy nämä 13 avainta, jotka vastaavat sivupalkin kohteiden nimiä: `Dashboard`, `DailySummaries`, `Chats`, `Contacts`, `Deals`, `Tasks`, `Automations`, `Campaigns`, `Appointments`, `Settings`, `Help`, `CreditsCounter` (sivupalkissa näkyvä hyvityssaldo) ja `guided_onboarding` (ohjattu määritystoiminto). Kolme muuta avainta — `AiInsights`, `Sub Accounts` ja `Agency Reselling` — hyväksytään, mutta ne eivät tee mitään: ne koskivat vain käytöstä poistettua perinteistä hallintapaneelia, joten niiden asettamisella ei ole vaikutusta alatileihisi. Puuttuvat avaimet tarkoittavat näkyvää; kun kirjaudut alatilille itse, piilotetut kohteet näytetään väliaikaisesti, jotta voit aina muuttaa asetukset takaisin.

Sivun piilottaminen valikosta ei koskaan anna pääsyä siihen. `Automations` vaatii, että `automations`-ominaisuus on myönnetty alitilille – jos asetat avaimen arvoon `true` ilman sitä, sivu ei silti näy. `Tasks` ja `DailySummaries` toimivat päinvastoin: ne ovat päällä jokaisella asiakkaalla, ellet kytke niitä pois päältä (katso [Poista tehtävät, päivittäiset yhteenvedot tai mediakirjasto käytöstä asiakkaalta](#turn-tasks-daily-summaries-or-the-media-library-off-for-a-client)).

***

## Valitse, minkä tyyppisiin kanaviin asiakas voi muodostaa yhteyden

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

Näkemäsi **Kanavatyypit**-kytkimet tilaustasolla ovat tavallisia ominaisuustunnuksia (feature ID), joten voit määrittää ne asiakaskohtaisesti API:n kautta hallintapaneelin sijaan. Tämä on yksi niistä päätepisteistä, joka nimeää alitilin omassa URL-osoitteessaan, joten se ei vaadi `sub_account_id`.

| Ominaisuuden tunnus | Kanava |
|---|---|
| `channel_chat_widget` | Verkkosivuston chat-widget |
| `channel_whatsapp_api` | WhatsApp Business API |
| `channel_whatsapp_web` | WhatsApp Web (QR-linkitetty numero) |
| `channel_instagram` | Instagram |
| `channel_messenger` | Facebook Messenger |
| `channel_telegram` | Telegram |
| `channel_line` | LINE |
| `channel_viber` | Viber |
| `channel_email` | Sähköpostilaatikko |
| `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"
    ]
  }'
```

Kolme asiaa, jotka on syytä huomioida:

- **Kutsu korvaa koko ominaisuusluettelon.** Lähetä jokainen ominaisuus, joka asiakkaalla tulisi olla, ei vain niitä, joita olet muuttamassa. Samat tunnukset toimivat `features`-kohtina `POST /v1/subaccounts`-palvelussa, kun luot tilin.
- **Kanavatyypit ja kanavien määrä ovat erillisiä rajoituksia, ja molemmat ovat voimassa.** `channels_1` / `channels_3` / `channels_unlimited` määrittävät yhteyksien *määrän*; `channel_*`-tunnukset määrittävät *sallitut tyypit*. Yllä oleva esimerkki tarkoittaa "enintään 3 yhteyttä, ja vain chat-widget, WhatsApp Web tai Instagram".
- **Jos et lähetä yhtään `channel_*`-tunnusta, kanavia ei rajoiteta.** Tämä on alkuperäinen toimintatapa, minkä vuoksi olemassa olevat asiakkaat eivät vaikuttaneet tähän muutokseen. Lähetä yksi tai useampi tunnus, niin kaikki muu näkyy asiakkaan Kanavat-sivulla lukittuna ja näyttää päivityskehotteen Yhdistä-painikkeen sijaan. Kanavat, jotka asiakas on jo yhdistänyt, toimivat edelleen.

> Kanavaluettelon määrittäminen **tilaustasolle**, jolloin jokainen kyseisen tason ostava asiakas perii sen, tehdään hallintapaneelissa toimistosi tilausasetuksissa. Tämä päätepiste määrittää sen yhdelle tietylle alitilille.

***

## Aseta asiakkaalle tarkka tiimin jäsenten määrä

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

`team_seats_*`-ominaisuudet tarjoavat vain esimääritettyjä porrasaskelmia (3 / 5 / 10 / rajoittamaton). Jos haluat antaa asiakkaalle **tarkan** määrän tiimipaikkoja — 2, 7, 15 tai mitä tahansa muuta — aseta sen sijaan `usage_limits.team_seats_limit`. Se ohittaa esimääritykset, ja alusta valvoo sitä jokaisen kutsun, suoran lisäämisen ja kutsun hyväksymisen yhteydessä: kun raja on saavutettu, uudet kutsut hylätään palvelinpuolella.

- Positiivinen kokonaisluku on tarkka yläraja.
- `0` tarkoittaa, että tiimin jäseniä **ei sallita** — asiakas ei voi kutsua ketään.
- `-1` tarkoittaa rajoittamatonta määrää.
- `null` tyhjentää mukautetun rajan ja palaa käyttämään sitä `team_seats_*`-esimääritystä, joka on ominaisuusluettelossa.

Rajan laskeminen ei koskaan poista olemassa olevia tiimin jäseniä; se vain estää uusien lisäämisen.

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

Voit asettaa tämän myös luontihetkellä: `POST /v1/subaccounts` hyväksyy `usage_limits.team_seats_limit` samalla semantiikalla. Lue nykyinen arvo hakemalla alitili `GET /v1/subaccounts?email=...`-toiminnolla ja tarkastelemalla `usage_limits.team_seats_limit`-kenttää (puuttuva/`null` = esiasetukset päättävät). Sama päätepiste päivittää myös `credits`, `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months`, `rollover_expiry_days` ja `byok_monthly_limit_usd` — lähetä vain ne avaimet, jotka haluat muuttaa.

**Miten tämä vaikuttaa SaaS-palvelupakettien käyttäjäpaikkarajoituksiin.** SaaS-palvelupaketeillasi voi olla oma käyttäjäpaikkakiintiö (määritetty pakettieditorissa – katso [Tiimin paikat paketissa](agency-accounts.md#step-3--set-up-pricing-tiers)), joka otetaan käyttöön automaattisesti, kun asiakas tilaa paketin. Tämän päätepisteen kautta asettamasi rajoitus lasketaan **manuaaliseksi** myönnytykseksi: paketin ostaminen korvaa sen paketin omalla käyttäjäpaikkakiintiöllä (kyseinen osto on nimenomainen valinta paketista), mutta automaattiset kuukausittaiset **uusimiset eivät koskaan korvaa manuaalista rajoitusta** – joten asiakkaalle myöntämäsi kertaluonteinen poikkeus säilyy laskutuskauden yli. Manuaalisen rajoituksen poistaminen kohdalla `null` palauttaa kentän hallinnan paketille seuraavan uusimisen yhteydessä.

**Rajoita sitä, mitä asiakas siirtää uusimisten välillä.** Kaksi muuta `usage_limits`-avainta sijaitsevat `roll_over_to_next_month`-avaimen vieressä. Molemmat hyväksytään myös `POST /v1/subaccounts`-toiminnolla luontihetkellä, ja `null` tyhjentää kumman tahansa.

| Avain | Mitä se tekee |
|---|---|
| `rollover_cap_months` | Kuukausien määrä, jonka asiakas saa pitää. Luku väliltä 0–120, murtoluvut sallittu (`0.5` = puoli kuukautta). Jokaisen uusimisen yhteydessä käyttämätön saldo karsitaan enintään tähän määrään kyseisen uusimisen myöntämästä saldosta ennen uusien krediittien lisäämistä; `0` ei siirrä mitään eteenpäin. |
| `rollover_expiry_days` | Kokonaisluku päivinä, 1–3650. Käyttämättä jääneet krediitit poistetaan ensimmäisessä uusimisessa sen jälkeen, kun ne saavuttavat tämän iän. Kulutus vähennetään aina vanhimmista krediiteistä ensin, joten asiakas, joka käyttää saldonsa joka kuukausi, ei koskaan menetä mitään. |

Jos näitä ei aseteta, molemmat palaavat asiakkaan suunnitelman oletusarvoihin; tässä lähetetty arvo ohittaa suunnitelman asetukset. Vain toistuvat krediitit (kuukausittainen saldo ja suunnitelman krediitit) ovat näiden alaisia: lisäostoksia, automaattisia latauksia ja kertaluonteisia lisäyksiä ei koskaan rajoiteta tai vanhenneta. Jokainen karsinta kirjataan asiakkaan krediittihistoriaan nimellä **Rollover Cap Credit Adjustment** tai **Expired Credits Credit Adjustment**, eikä se koskaan lasketa käytöksi. Suunnitelmatason vastineet ovat `rollover_cap_months` ja `rollover_expiry_days` hinnoittelutasolla — katso [The fields on a tier](#the-fields-on-a-tier) ja [Capping what rolls over](sub-accounts.md#capping-what-rolls-over).

***

## Aseta asiakaskohtainen tekoälyn hinnoittelu ja käytännöt

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

Kahdeksan muuta asiakaskohtaista kytkintä, `/limits`, `/features` ja `/menu-visibility` lisäksi. Jokainen käyttää alitilin uid-tunnusta URL-osoitteessa (ei `sub_account_id` runko-/kyselyparametria — kohde on jo nimetty polussa) ja on rajattu samalla tavalla: toimistoavaimesi, ja alitilin on kuuluttava toimistoosi.

**Mitä tekoälymalleja asiakas voi käyttää**

```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 }` sallii asiakkaan käyttää (tai poistaa käytöstä) Max AI -tasoa — infrastruktuuriamme alustan listahintaan. Tämän kytkeminen päälle BYOK-asiakkaalle muuttaa heidän tekoälykustannuksensa "ilmaisesta omalla avaimella" muotoon "veloitetaan krediittipoolistani", joten se on harkittu asiakaskohtainen päätös eikä toimistotason oletus.

Jos haluat rajoittaa, MITÄ tasoja asiakkaan kampanjat ja agentit voivat valita (sen sijaan, että vain rajoittaisit Max-tasoa), käytä `ai-tiers`-päätepistettä:

```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` on taulukko, joka on johdettu arvoista `standard`, `economy`, `max`, `mini` — se KORVAA asiakkaan sallittujen listan. Lähetä `null` (tai `[]`) tyhjentääksesi rajoituksen ja salliaksesi minkä tahansa tason valinnan. Tämä on tärkeää, koska alitili, joka valitsee oman tekoälytasonsa, kuluttaa **sinun** krediittipooliasi, joten tämä on keino hallita, mitä malleja jälleenmyyjäasiakas voi käyttää laskusi kasvattamiseen.

**Vastausviesti, kun asiakkaalla ei ole krediittejä**

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

Kun asiakkaan saldo (tai poolisi) on tyhjä, tekoäly ei voi vastata eikä yhteyshenkilö kuule mitään. `enabled: true`-asetuksella jokainen yhteyshenkilö, joka kirjoittaa katkoksen aikana, saa `message`-viestin kerran (enintään 500 merkkiä, lähetetään sellaisenaan jokaisessa kanavassa), ja tekoäly vastaa näihin keskusteluihin oikeasti, kun krediitit ovat palanneet. `enabled: false` säilyttää tallennetun tekstin myöhempää käyttöä varten; `enabled: false` ilman `message`-arvoa poistaa asetuksen. Sama kytkin kuin **Holding reply when out of credits** alitilin muokkausikkunassa — katso [A holding reply while a client is out of credits](sub-accounts.md#a-holding-reply-while-a-client-is-out-of-credits).

**Mitä asiakas maksaa tekoälytoiminnosta ja WhatsApp-maksun lisämaksu**

Kaksi tapaa asettaa asiakashintasi, yksinkertaisimmasta tarkimpaan:

```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` on hinta krediitteinä, jonka alitilin OMA saldo kuluttaa per Max-mallin tekoälytoiminto — asiakashintasi lisämaksu sen päälle, mitä poolisi todellisuudessa maksaa. `null` palauttaa ohituksen takaisin alustan listahintaan. Hinnan on oltava vähintään se, mitä Max-toiminto maksaa omalle poolillesi (joten et voi koskaan hinnoitella asiakasta alle omien kustannustesi) ja enintään 10 krediittiä; tätä rajaa ylittävä pyyntö hylätään virheilmoituksella, joka sisältää lasketun alarajan.

Jos haluat käyttää toimintotyyppikohtaista hinnoittelua yhden kiinteän Max-hinnan sijaan, käytä `action-pricing`-päätepistettä:

```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` on YHDISTÄMINEN asiakkaan olemassa olevaan karttaan — avain, jota et mainitse, säilyy ennallaan, ja `null` palauttaa kyseisen avaimen oletusarvoonsa. Tunnistetut avaimet:

| Avain | Hinnat |
|---|---|
| `AI_MESSAGE` | Tekoälyvastaus |
| `AI_TOOL_USE` | Tekoälytyökalukutsu |
| `EVALUATION_CALL` | Chat-arviointikierros |
| `INTERRUPTION_HANDLING` | Keskeytyksen käsittely vastauksen aikana |
| `CONTACT_TAG` | Tekoälyn määrittämä yhteystietotunniste |
| `CHAT_SUMMARY` | Chat-yhteenveto |
| `wa_carrier_multiplier` | Mark-up-kerroin, jota sovelletaan jokaiseen ei-tekoälypohjaiseen WhatsApp-maksuun, jonka asiakas maksaa: kuukausittainen numerovuokra, hallitun kaistan toimitusmaksut ja Meta/Twilio-mallien läpikulku-kustannukset. |

Toimintokohtaisten hintojen on oltava yli 0 ja enintään 10; `wa_carrier_multiplier` on oltava vähintään `1` (ei alennusta kustannusten alapuolelle) ja enintään 10. Tunnistamattoman avaimen tai sallitun alueen ulkopuolisen arvon lähettäminen hylkää KOKO pyynnön ja nimeää jokaisen virheellisen avaimen, joten kirjoitusvirhe ei voi koskaan tallentaa huomaamatta hintaa, jota ei todellisuudessa sovelleta.

Jos olet [Champions Circle](https://skool.com/dm-champions) -jäsen, `insider-rate` siirtää 20 %:n Max/Lead Finder -alennuksesi yhdelle asiakkaalle sen sijaan, että se koskisi koko toimistoa:

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

Sen kytkeminen päälle edellyttää, että omalla toimistotililläsi on voimassa oleva Circle-jäsenyys; sen kytkeminen pois päältä ei koskaan vaadi sitä, joten jäsenyytensä menettänyt käyttäjä voi aina palauttaa asiakkaan asetukset.

**Lukitse asiakkaan pelikirjan osioita**

```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` on taulukko, joka on koottu arvoista `instructions`, `goal`, `rules`, `personality`, `conclude_unless` — se KORVAA asiakkaan lukitun luettelon. Palvelin hylkää lukitun osion, jos ALITILI itse yrittää muuttaa sitä (suoraan tai API-avaimella), kun taas sinä (käyttäen `sub_account_id`) ja asiakkaan oma hallintapaneelin ylläpitonäkymä voitte edelleen muokata mitä tahansa. Lähetä `null` (tai `[]`) avataksesi kaiken. Hyödyllinen "done-for-you"-asiakkaille, joiden pelikirjan omistat ja joiden tuloksista sinut arvioidaan.

**Määritä asiakkaan ilmoitusasetukset heidän puolestaan**

```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` korvaa asiakkaan koko ilmoitusasetusten joukon (ei avainkohtainen yhdistäminen — lähetä jokainen kategoria, jonka haluat säilyttää, vastaten sitä, miten alitilin oma Asetukset-sivu tallentaa ne). Jokainen `settings`-kohdan alla oleva kategoria hyväksyy arvon `enabled` (boolean) ja enintään kolme `channels`-arvoa kohteista `email`, `in_app`, `webhook`. Lähetä `null` palauttaaksesi alustan oletusasetukset.

Kaikki seitsemän päätepistettä vastaavat `{ "success": true, "data": { "subAccountUid": "...", ...the field(s) you set... } }`, ja niistä jää auditointiloki, jossa näkyy arvo ennen ja jälkeen muutoksen. Yleisiä virheitä: `403`, jos tilisi ei ole Agency/Dev tai alitili ei ole hallinnoitavissasi, `400`, jos kyseessä ei ole toimiston alitili tai arvo on sallitun alueen ulkopuolella.

***

## Keskeytä asiakas, joka on jäädyttänyt tilauksensa

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

Kun asiakas jäädyttää tilauksensa kanssasi, keskeytä hänen tilinsä sen poistamisen sijaan: kaikki heidän lähettämänsä viestit pysähtyvät välittömästi — lähtevät viestit, lähetykset, tekoälyvastaukset kaikissa kanavissa — ja kun he kirjautuvat sisään, he näkevät sovelluksen sijaan koko näytön **Tili keskeytetty** -lukitusnäytön (valinnaisella viestilläsi). Mitään ei poisteta tai katkaista: agentit, kampanjat, yhdistetyt kanavat, yhteystiedot ja keskusteluhistoria pysyvät täsmälleen ennallaan, joten keskeytyksen poistaminen palauttaa asiakkaan täsmälleen siihen, mihin hän jäi — asetuksia ei tarvitse tehdä uudelleen.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `message` | Ei | Näytetään asiakkaalle lukitusnäytöllä. Jätä tyhjäksi oletusviestiä varten. |
| `reason` | Ei | Toimiston sisäinen huomautus, joka tallennetaan keskeytyksen yhteydessä ja lokiin — ei näytetä koskaan asiakkaalle. |

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

**Vastaus**

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

Kun asiakas palaa, `POST /v1/subaccounts/SUB_ACCOUNT_UID/unpause` (ei runkoa) poistaa lukituksen — viestien lähettäminen ja tekoälyvastaukset jatkuvat välittömästi.

Hyvä tietää:

- **Tämä on sama tila kuin hallintapaneelin Hard blocked -kytkin** ([Alitilin estäminen / keskeyttäminen](sub-accounts.md#blocking-pausing-a-sub-account)) — API:n kautta keskeytetty asiakas näkyy estettynä hallintapaneelissa ja päinvastoin, ja keskeytyksen poistaminen poistaa kummallakin tavalla asetetun eston. Nykyinen tila on luettavissa `agency_block`-kentästä kohteessa `GET /v1/subaccounts` (`level` kohteesta `"none"`, `"soft_blocked"` tai `"hard_blocked"`).
- **Molemmat kutsut ovat idempotentteja.** Jo keskeytetyn asiakkaan keskeyttäminen vain päivittää viestin, syyn ja aikaleiman; aktiivisen asiakkaan keskeytyksen poistaminen ei muuta mitään.
- **Asiakkaalle ei lähetetä automaattisesti sähköpostia** — monet toimistot käyttävät white-label-ratkaisuja, joten asiakkaalle tiedottaminen jää sinun tehtäväksesi.
- **Oma DM Champ -laskutuksesi pysyy ennallaan.** Asiakkaan keskeyttäminen vaikuttaa vain sinun ja asiakkaan väliseen suhteeseen.
- **Tekoälyavustajat voivat tehdä tämän myös**: [MCP-palvelin](../integrations/connect-ai-clients.md) tarjoaa nämä päätepisteet `pause_subaccount`- ja `unpause_subaccount`-työkaluina.

***

## Myönnä tai vähennä krediittejä suoraan

`POST /v1/subaccounts/credits`

Lisää tai poistaa tarkan määrän krediittejä yhden alitilin saldosta — API-vastine hallintapaneelin manuaaliselle krediittien säädölle. Tämä on kertaluonteinen saldomuutos, joka eroaa toistuvista `monthly_credits`, `roll_over_to_next_month`, `rollover_cap_months` ja `rollover_expiry_days` -asetuksista kohdassa [`PUT /v1/subaccounts/{subAccountUid}/limits`](#set-an-exact-team-member-limit-for-a-client).

Tämä on tämän sivun ainoa päätepiste, joka tunnistaa alitilin **sähköpostiosoitteen** eikä `sub_account_id`:n perusteella.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `email` | Kyllä | Alitilin sähköpostiosoite sellaisena kuin se on toimistosi alla. |
| `amount` | Kyllä | Nollasta poikkeava krediittien määrä. Positiivinen lisää, negatiivinen vähentää. |
| `description` | Ei | Näytetään säädön yhteydessä asiakkaan krediittihistoriassa. Oletuksena on yleinen "Toimiston API-kautta tekemä säätö" -rivi. |

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

**Vastaus**

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

Negatiivinen `amount`, joka laskisi saldon nollan alapuolelle, hylätään `400`-vastauksella, joka kertoo käytettävissä olevan saldon ja summan, jota yritit vähentää. Jos asiakas käyttää omaa Stripe-laskutustaan (jälleenmyyntitila), lisätty summa lasketaan myös heidän ostamikseen krediiteiksi, joten se säilyy seuraavassa kuukausittaisessa nollauksessa samalla tavalla kuin oikea lisäosto; tavallisella allokoidulla asiakkaalla se käsitellään osana heidän toistuvaa saldoaan. Kummassakin tapauksessa ne ovat kertaluonteisia lisäyksiä, joten tilille (tai sen suunnitelmaan) asetettu siirtoraja tai vanheneminen ei koskaan karsi niitä — vain toistuva saldo ja suunnitelman krediitit ovat niiden alaisia.

***

## Lue alitilin keskustelut

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

Mahdollistaa asiakkaan keskustelujen seuranta- tai tukinäkymän luomisen ilman kirjautumista heidän tililleen. Listaa ensin heidän yhteystietonsa ja viimeisimmän viestin esikatselu, ja lue sitten yhden yhteystiedon koko viestihistoria.

**Listaa yhteystiedot**

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

| Kyselyparametri | Pakollinen | Kuvaus |
|---|---|---|
| `pageSize` | Ei | Yhteystietojen määrä sivua kohden. Oletus 25, enintään 50. |
| `lastActivityAt` | Ei | Sivutusosoitin – käytä edellisen sivun `lastActivityAt`-arvoa jatkaaksesi. |
| `searchQuery` | Ei | Suodata yhteystiedon nimen tai puhelinnumeron perusteella. |

**Vastaus**

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

Yhteystiedot on järjestetty uusimman toiminnan mukaan. Jatka sivutusta `lastActivityAt`-arvolla, kun `hasMore` on `true`.

**Lue yhden yhteystiedon viestit**

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

| Kyselyparametri | Pakollinen | Kuvaus |
|---|---|---|
| `pageSize` | Ei | Viestien määrä sivua kohden. Oletus 30, enintään 100. |
| `beforeTimestamp` | Ei | Sivutusosoitin – hae tätä ISO-aikaleimaa vanhempia viestejä. |

**Vastaus**

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

Viestit palautetaan uusimmasta alkaen; selaa historiaa taaksepäin `beforeTimestamp`-arvolla.

***

## Lue krediittien käyttö ja kampanjoiden tila koko asiakaskunnassasi

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

Kaksi koontinäyttötyyppistä yhteenvetoa kaikista hallinnoimistasi alatileistä, joiden avulla voit luoda oman toimistoraportoinnin sen sijaan, että klikkailisit jokaisen asiakkaan kohdalla erikseen.

**Krediittien käyttö**

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

| Kyselyparametri | Pakollinen | Kuvaus |
|---|---|---|
| `from` / `to` | Kyllä | ISO-päivämääräväli. |
| `subAccountId` | Ei | Jätä pois saadaksesi koko toimiston yhteenvedon, yksi rivi per alatili. Lisää vaihtaaksesi yksityiskohtaiseen tilaan: kyseisen alatilin yhteenveto sekä sen raa'at, sivutetut käyttömerkinnät. |
| `limitCount` | Ei | Vain yksityiskohtainen tila. Oletus 500, enintään 2000. |
| `startAfterTimestamp` | Ei | Vain yksityiskohtainen tila – sivutusosoitin. |

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

Kun käytät `subAccountId`-parametria, sama vastaus sisältää myös `records`-tiedot: yksittäiset veloitukset, joissa on `amount`, `reason`, `campaignName`, `contactName` ja `timestamp`. Asiakkaalta, joka käyttää omaa BYOK-avaintaan krediittiesi sijaan, on piilotettu kustannus-/tunnusluvut (`costsRedacted: true`) – kyseessä on alustan kustannustelemetria, jota ei ole tarkoitettu jälleenmyyjän näkymään. |

**Kampanjan tila**

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

| Kyselyparametri | Pakollinen | Kuvaus |
|---|---|---|
| `pageSize` | Ei | Alitilien määrä sivua kohden. Oletus 10, enintään 50. |
| `lastDocumentId` | Ei | Sivutuskursori. |
| `searchQuery` | Ei | Suodata alitilin nimen tai sähköpostin perusteella. |

```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` -lippu merkitsee alitilit, jotka kannattaa tarkistaa – esimerkiksi keskeytetty kampanja tai kampanja, johon ei ole reititetty kanavaa. Käytä tätä rakentaaksesi koko asiakaskunnan kattavan tilannekuvan sen sijaan, että avaisit jokaisen asiakkaan tiedot pysähtyneen kampanjan havaitsemiseksi.

> Aikasarjaviestejä ja krediittitoimintaa varten kaikilla asiakkailla (kaavioita varten valmis sarja eikä vain tietyn ajanhetken tilannekuva), katso `GET /analytics/agency-rollup` Analytics API -oppaasta.

***

## Toimita asiakastili, joka on jo määritetty

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

[Tilannevedos](snapshots.md) on uudelleenkäytettävä malli: yksi tai useampi tekoälyagentti sekä niiden tietokanta, työkalut ja media, jotka on tallennettu omalta tililtäsi. Kaksi päätepistettä lisäävät sen tarjoamisprosessiisi.

**Automaattinen — jokainen uusi asiakas saa sen heti.** Merkitse tilannevedos oletukseksi kerran, niin jokainen luomasi tili sisältää sen asennettuna tästä eteenpäin. Tämä koskee tilejä, jotka on luotu `POST /v1/subaccounts` kautta, hallintapaneelissa luotuja tilejä sekä tilejä, jotka luodaan automaattisesti, kun asiakas maksaa kassalinkkisi kautta.

Etsi ensin tilannevedoksen tunnus (id):

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

Aseta se sitten oletukseksi:

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

Siinä on koko integraatio. Lähetä `{"snapshot_id": null}` kytkeäksesi sen pois päältä. Voit tehdä saman hallintapaneelista napsauttamalla tähteä **Snapshots**-sivulla.

Jos haluat lukea, mitkä ovat tällä hetkellä tähdellä merkittyjä (esimerkiksi ennen kuin provisiointiskripti päättää, asetetaanko sellainen), `GET /v1/snapshots/default` palauttaa `{ "success": true, "data": { "default_snapshot_id": "SNAPSHOT_ID" } }` – `null`, kun mitään ei ole merkitty tähdellä. `GET /v1/snapshots` (jota käytetään yllä olevan tunnisteen löytämiseen) palauttaa saman `default_snapshot_id` yhdessä täyden `snapshots` -taulukon kanssa, joten useimmat integraatiot tarvitsevat vain yhden kutsun. Täyden tilannekuvaobjektin kentät löytyvät [Snapshots](snapshots.md) -oppaasta.

**Tarvittaessa — asenna yhdelle tilille.** Hyödyllinen olemassa olevan asiakkaan käyttöönotossa tai kun haluat antaa asiakkaalle toisen mallin myöhemmin.

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

Jätä `sub_account_id` pois, niin se asennetaan omaan toimistotiliisi. Kuten `/subaccounts`-päätepisteet, nämä nimeävät kohdetilin polussa tai rungossa sen sijaan, että käyttäisivät ympäristön `sub_account_id`-parametria.

Hyvä tietää ennen kuin rakennat sen varaan:

- **Asennetut agentit käynnistyvät keskeytettyinä.** Yhdistä ensin asiakkaan kanavat ja aktivoi sitten agentti. Tämä pätee sekä automaattiseen että tarvittaessa tehtävään asennukseen.
- **Tarjoaminen ei koskaan epäonnistu tilannevedoksen vuoksi.** Jos asennus ei valmistu, asiakastili luodaan silti ja se on käyttökelpoinen – se saapuu vain tyhjänä, ja voit käyttää tilannevedoksen myöhemmin.
- **Kanavia, kalentereita ja OAuth-yhteyksiä ei koskaan kopioida.** Jokainen tili yhdistää omansa. Tavallista API-avainta käyttävät työkalut toimivat heti.
- **Kahdesti käyttäminen luo toisen kopion.** Mitään ei ylikirjoiteta.

***

## Rakenna itse mallipohja API:n kautta

`POST /v1/snapshots` · agentit, mukautetut funktiot ja media API:n kautta

Edellinen osio käsittelee hallintapaneelissa luodun tilannevedoksen jakelua. Myös luontipuoli on käytettävissä, joten koko prosessi — pääasetusten kokoaminen kerran, niiden tallentaminen ja jakaminen jokaiselle asiakkaalle — voidaan suorittaa koodin avulla.

Osat siinä järjestyksessä kuin provisiointiskripti niitä käyttää:

1. **Luo mukautetut funktiot.** `POST /v1/custom-functions` luo sellaisen; `GET /v1/custom-functions` listaa olemassa olevat, ja `GET`, `PUT` sekä `DELETE` kohteessa `/v1/custom-functions/{customFunctionId}` lukevat, päivittävät ja poistavat niitä. `POST /v1/custom-functions/test` testaa määrityksen ennen tallennusta.
2. **Luo ja muokkaa agenttia.** `POST /v1/agents` luo sen, `PUT /v1/agents/{agentId}` päivittää sen, ja `PATCH /v1/agents/{agentId}/active` yhdessä `{ "active": false }` kanssa pitää sen tauotettuna työn aikana (sama kutsu `true`-parametrilla aktivoi sen). `GET /v1/agents` listaa agentit.
3. **Anna agentille sen kyvyt.** `POST /v1/agents/{agentId}/custom-functions` yhdessä `{ "custom_function_id": "..." }` kanssa liittää funktion agenttiin; vastaava `DELETE /v1/agents/{agentId}/custom-functions/{customFunctionId}` irrottaa sen.
4. **Täytä mediakirjasto.** `POST /v1/agents/{agentId}/media-library` lataa kohteen (JSON, jossa `base64Data`, `mimeType`, `title`, `description`); `GET` listaa agentin kohteet, ja `PATCH`/`DELETE` kohteessa `/{itemId}` päivittävät tai poistavat niitä.
5. **Tallenna se tilannevedoksena.**

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

Tämän jälkeen toimitaan kuten edellisessä osiossa: aseta se oletukseksi, jotta jokainen uusi asiakas saa sen automaattisesti, tai käytä sitä tarpeen mukaan. Hallintatoiminnot ovat myös käytettävissä: `PATCH /v1/snapshots/{snapshotId}` yhdessä `{ "name": "..." }` kanssa nimeää tilannevedoksen uudelleen, `DELETE /v1/snapshots/{snapshotId}` poistaa sen (ja poistaa oletusasetuksen, jos se oli käytössä), ja `GET /v1/snapshots/apply-targets` listaa kaikki tilit, joihin voit asentaa.

Agentti-, mukautettu funktio- ja mediapäätepisteet hyväksyvät kaikki `sub_account_id`-parametrin, joten samoilla kutsuilla voidaan ylläpitää agenttia suoraan yhden asiakkaan tilillä. Tilannevedos-kutsut kohdistuvat aina toimistotilillesi — mallipohja säilyy sinulla. Täydelliset pyyntö- ja vastausskeemat kaikille näille löytyvät [API-viitteestä](../api/reference.md).

***

## Hallitse hinnoittelutasojasi API:n kautta

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

SaaS-tilassa (**SaaS Mode → Pricing Tiers**) myymiäsi suunnitelmia voi lukea ja muuttaa koodilla, joten oma hallintapaneelisi tai provisiointiskriptisi voi lisätä suunnitelman, säätää hintaa tai jakaa maksulinkin ilman, että kenenkään tarvitsee avata hallintapaneelia. Tunnistaudu toimistosi API-avaimella kuten kaikissa muissakin tämän sivun kutsuissa; nämä päätepisteet ovat toimistotasoisia, joten ne eivät vaadi `sub_account_id`-arvoa. Jokainen kirjoitusoperaatio suorittaa saman validoinnin ja saman Stripen tuote- ja hintasynkronoinnin kuin hallintapaneelissa tallentaminen, joten täällä luotu suunnitelma on täysin samanlainen kuin käsin luotu.

### Listaa tasosi

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

**Vastaus**

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

Jokainen taso palautetaan **`tierIndex`**-tunnisteellaan — se on tason sijainti suunnitelmalistassasi, ja muut kolme kutsua käyttävät tätä tunnisteena — sekä valmiilla **`checkout_url`**-linkillä. Se on sama linkki, jonka **Maksut**-välilehti antaa, ja se osoittaa valmiiksi [white label -verkkotunnukseen](white-labeling.md), jolla kyseistä tasoa myydään.

### Lisää taso

Runko on yksi taso-objekti; se lisätään listasi loppuun.

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

Vastaus sisältää luodun tason, mukaan lukien sen `tierIndex`-sijainnin ja `checkout_url`-linkin.

### Muokkaa tasoa

Lähetä vain ne kentät, joita haluat muuttaa; kaikki muu suunnitelmassa säilyy ennallaan.

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

API:n tunnistamattomat kentät hylätään sen sijaan, että ne jätettäisiin huomiotta, ja virheilmoitus nimeää kyseisen kentän — näin kirjoitusvirhe ei voi koskaan hiljaa kirjoittaa asetusta, joka näyttää toimivalta mutta ei tee mitään. Hinnan, krediittien, valuutan tai laskutusvälin muuttaminen luo uuden hinnan Stripe-tilillesi; asiakkaat, jotka ovat jo tilanneet palvelun, pysyvät siinä suunnitelmassa, johon he alun perin liittyivät.

### Poista taso

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

Sama sääntö kuin hallintapaneelissa: suunnitelmaa, jolla on vielä aktiivisia tilaajia, ei voi poistaa. Pyyntö hylätään, ja saat tiedon siitä, kuinka monta tilaajaa suunnitelmalla on — peruuta tai siirrä heidät ensin. Onnistuneen poiston jälkeen vastaus sisältää jäljellä olevat tasosi uudelleen numeroituina.

### Tason kentät

| Kenttä | Mitä se tarkoittaa |
|---|---|
| `label` / `description` | Suunnitelman nimi ja valinnainen rivi, joka näkyy kassasivullasi. |
| `credits` | Krediitit, jotka asiakas saa **kuukaudessa** kuukausi- tai vuosisuunnitelmassa, ja **laskutuskaudessa** viikkosuunnitelmassa. |
| `price_cents` | Hinta laskutusjaksoa kohden, pienimmässä valuuttayksikössä (`2900` = 29,00 $). Vuosisuunnitelmassa tämä on koko vuoden hinta. |
| `currency` | Pienillä kirjaimilla kirjoitettu ISO-koodi — `usd`, `eur`, `gbp` ja niin edelleen. |
| `billing_interval` / `billing_interval_count` | `month` (oletus), `year` tai `week`, jossa on määrä 1–52 "joka N. viikko" -toistolle. |
| `trial_days` | Ilmaisen kokeilujakson pituus, 0–90. `0` (tai sen pois jättäminen) tarkoittaa, ettei kokeilua ole. |
| `trial_credits` | Krediitit, joilla asiakas aloittaa kokeilun. Oletusarvona on suunnitelman `credits`. |
| `trial_card_required` | `false` antaa asiakkaan aloittaa kokeilun ilman korttitietojen syöttämistä. Oletusarvona `true`. |
| `trial_hard_expiry` | `true` palauttaa käyttämättömät kokeilukrediitit pooliisi ja lukitsee asiakkaan tilin, kun kokeilu päättyy ilman päivitystä. Oletusarvona `false` — katso [Hard expiry after trial](agency-accounts.md#step-3--set-up-pricing-tiers). |
| `rollover_cap_months` | Kuukausien määrä, jonka tämän suunnitelman asiakkaat voivat siirtää uusimisten välillä — luku väliltä 0–120, murtoluvut sallittu. `0` ei siirrä mitään; `null` (oletus) tarkoittaa, ettei rajaa ole. Katso [Capping what rolls over](sub-accounts.md#capping-what-rolls-over). |
| `rollover_expiry_days` | Päivät, joiden jälkeen käyttämättömät krediitit poistetaan seuraavassa uusimisessa — kokonaisluku väliltä 1–3650. `null` (oletus) tarkoittaa, etteivät ne koskaan vanhene. |
| `features` / `feature_settings` | Mitä tämän suunnitelman asiakkaat saavat — samat ominaisuustunnukset kuin kohdassa [Choose which channel types a client can connect](#choose-which-channel-types-a-client-can-connect). |
| `team_seats_limit` | Tiimipaikat, jotka suunnitelma myöntää: tarkka luku, `0` ei yhtään, `-1` rajoittamaton. |
| `white_label_config` | Millä [white label -verkkotunnuksistasi](white-labeling.md#up-to-three-white-labels) suunnitelmaa myydään. |

Kokeilujakson kentillä on merkitystä vain tilauksissa, joissa on kokeilujakso: jos tallennat tason arvolla `trial_days: 0`, ne poistetaan. Tilauksen Stripe-tuote- ja hintatunnisteet hallinnoidaan puolestasi, eikä niitä voi asettaa käsin.

Kolme asiaa, jotka on syytä huomioida:

- **Tasoindeksit ovat sijainteja, eivät pysyviä tunnisteita.** Tilauksen poistaminen siirtää kaikkia sen jälkeisiä tilauksia yhdellä eteenpäin, joten hae luettelo uudelleen jokaisen muutoksen jälkeen — ja kopioi julkaisemasi maksulinkit uudelleen, aivan kuten tekisit poistettuasi tilauksen hallintapaneelista.
- **SaaS-tila on määritettävä ensin.** Nämä päätepisteet edellyttävät toimistotiliä, jossa on valkoinen merkintä (white labeling) ja tallennettu Stripe-avain; ilman näitä tilauksen tuotteelle ja hinnalle ei ole Stripe-tiliä, johon ne voisivat kuulua.
- **Kaksikymmentä tilausta on yläraja**, sama kuin hallintapaneelissa. Luettelovastauksen `max_tiers`-kenttä kertoo nykyisen rajan.

Täydelliset pyyntö- ja vastausskeemat löytyvät [API-viitteestä](../api/reference.md) kohdasta **Agency**.

***

## Aseta krediittikohtainen hintasi API:n kautta

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

Hinta, jonka asiakkaat maksavat kertaluonteisista lisäyksistä (**SaaS-tila → Krediittikohtainen hinnoittelu**), voidaan lukea ja muuttaa myös koodin kautta. Tämä on tarkoitettu tilanteisiin, joissa hinnan on muututtava automaattisesti: toimisto, joka myy krediittejä yhdessä valuutassa mutta laskuttaa toisessa, voi antaa ajastetun tehtävän päivittää hinnan valuuttakurssien muuttuessa, sen sijaan että joku muokkaisi sitä käsin joka viikko.

### Lue nykyinen hinta

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

**Vastaus**

```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` on alhaisin hinta, jonka alusta kyseisessä valuutassa sallii, joten tehtävä voi tarkistaa uuden hinnan ennen sen lähettämistä. Kaikki kolme arvoa ovat `null`, kunnes hinta on asetettu.

### Muuta sitä

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

Lähetä vain se, mitä haluat muuttaa. `price_per_credit_cents` on hinta valuutan pienimmässä yksikössä (`130` = 1,30 R$); `currency` on ISO-koodi pienillä kirjaimilla; `note` on valinnainen, enintään 200 merkin mittainen rivi, joka näytetään asiakkaille suoraan krediittikohtaisen hinnan alla heidän Laskutus-sivullaan – kätevä viitehinnan näyttämiseen toisessa valuutassa, kuten *"0,25 USD per krediitti viitekurssillamme"*. Lähetä `"note": ""` poistaaksesi sen. Vastaus on samassa muodossa kuin yllä oleva luku, joten tehtävä voi verrata arvoja ja ohittaa kirjoituksen, jos mikään ei ole muuttunut.

Samat säännöt pätevät kuin hallintapaneelissa: hinta ei voi alittaa kyseisen valuutan alustakohtaista minimiä, ja tilillä on oltava white labeling -ominaisuus käytössä. Toisin kuin hinnoittelutaso-päätepisteissä, tämän arvon lukemiseen tai muuttamiseen ei vaadita Stripe-avainta.

### Anna tehtävälle avain, joka ei voi tehdä mitään muuta

Koko toimistoavaimen syöttäminen ajoittimeen antaa enemmän käyttöoikeuksia kuin hinnan päivitys vaatii. Luo sen sijaan **rajattu avain** (scoped key), joka on rajoitettu vain **Agency Credit Price** -alueeseen: kyseinen avain voi lukea ja muuttaa krediittikohtaista hintaa, mutta ei mitään muuta — se ei pääse käsiksi alatileihin, tilauksiin, krediitteihin tai Stripe-yhteyteesi.

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

Vastaus sisältää uuden avaimen kohdassa `api_key` **vain kerran** — sitä ei näytetä enää uudelleen, joten tallenna se heti. Aseta `"read_only": true` avaimelle, jonka tarvitsee vain lukea hintaa, ja lisää `"expires_at"` (ISO-päivämäärä), jos haluat sen lakkaavan toimimasta automaattisesti. Vain tilin omistajan avain voi luoda rajattuja avaimia; listaa tai peruuta ne käyttämällä `GET /v1/api-keys` ja `DELETE /v1/api-keys/{id}`.

***

## Salli alitilin lukea hinnoittelusi

`GET /v1/subaccounts/agency-pricing`

Jokaista muuta tämän sivun päätepistettä kutsutaan **toimistoavaimellasi**, kohdistaen tarvittaessa asiakkaan `sub_account_id` kautta. Tämä on päinvastainen: sitä kutsutaan **alitilin omalla API-avaimella**, ilman `sub_account_id`, joten asiakkaan oma täydennyssivu (tai integraatio, jonka rakennat heille) voi näyttää heille veloittamasi hinnat näkemättä koskaan toimistotiliäsi.

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

**Vastaus**

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

Tämä vastaa täsmälleen sitä, mitä [`GET /v1/agency/pricing-tiers`](#list-your-tiers) ja [`GET /v1/agency/credit-price`](#read-the-current-price) palauttavat sinulle toimistona, miinus kaikki se, mitä asiakkaan ei tarvitse nähdä (Stripe-tunnisteet, `max_tiers` ja niin edelleen). Se toimii vain tilille, joka on todellisuudessa alitili, jolla on linkitetty toimisto – sen kutsuminen omalta toimistotililtäsi palauttaa käyttöoikeusvirheen.

***

## Huomioitavaa

- **Käytä toimistoavaintasi.** Todenna jokainen kutsu toimistotilisi API-avaimella – älä alitilin avaimella. `sub_account_id`-parametri ohjaa toiminnon oikeaan paikkaan.
- **Krediitit veloitetaan alitililtä.** Ostot ja toistuvat maksut veloitetaan kohdistetun alitilin krediittisaldosta, ei sinun saldostasi.
- **`404` tarkoittaa "ei sinun alitilisi".** Tarkista tunnus ja varmista, että tili on hallinnoimasi tili.
- **Parametri on valinnainen kaikkialla, missä se on käytössä.** Jos jätät sen pois, sama päätepiste toimii toimistotililläsi, joten voit käyttää samaa integraatiota molempiin.

***

## Aiheeseen liittyvää

- [API-pääsy](../integrations/api-access.md) – todennus, perus-URL, virheet, nopeusrajoitukset.
- [Alitilit](sub-accounts.md) – listaa ja hallitse tilejä, joita voit kohdistaa.
- [Alitilin automaattinen lataus](sub-account-auto-recharge.md) – myönnä krediittejä alitilille webhookin + API:n kautta.
- [Kampanjoiden API](../api/campaigns.md) – luo, päivitä ja kopioi kampanjoita, mukaan lukien täydellinen kenttäviite.
- [Kanavayhteyden API](../api/channels.md) – yhdistä asiakkaan kanavat ja reititä ne kampanjaan.
- Analytics API -opas (API-osiossa) – toimiston alitilien yhteenveto ja kaikki muut raportointipäätepisteet.
- [Tilannekuvat](snapshots.md) – mitä tilannekuva tallentaa ja miten sellainen rakennetaan hallintapaneelissa.
