
# Viestit ja keskustelut

Messages API:n avulla voit lähettää viestin mille tahansa yhteyshenkilölle, lukea keskusteluhistorian, korjata tai poistaa jo lähettämäsi viestin, reagoida viestiin, hakea koko keskusteluketjun, viedä keskustelun tekstimuodossa ja merkitä keskusteluja luetuiksi tai lukemattomiksi – kaikki tämä ilman, että sinun tarvitsee avata postilaatikkoa.

Kaikki tämän sivun polut ovat suhteessa perus-URL-osoitteeseen `https://api.youraiconnector.com/v1`. Jokainen pyyntö vaatii API-avaimesi – katso [todennus](authentication.md) nähdäksesi täydellisen listan tavoista lähettää se. Alla olevissa esimerkeissä käytetään `X-API-Key`-otsikkoa, ja yhdessä cURL-esimerkissä näytetään myös `?apiKey=`-kyselylomake.

> **Miten toimitus toimii:** Viestin lähettäminen **ei** odota sen perillemenoa. API vastaanottaa viestisi, palauttaa välittömästi viestin tunnisteen ja toimittaa sen sitten taustalla yhteyshenkilön kanavalla (WhatsApp, SMS, Instagram jne.). Jos haluat seurata, onko viesti todella toimitettu tai luettu, kuuntele tilapäivityksiä [Webhookien](webhooks.md) avulla – älä käytä kyselyitä (polling). Lähetysvastaus vahvistaa vain, että viesti on otettu vastaan.

---

## Lähetä viesti

Lähettämiseen on kaksi tapaa. Valitse se, joka sopii parhaiten tapaasi tunnistaa yhteyshenkilö:

- **Lähetä yhteyshenkilön tunnisteella** – tiedät jo yhteyshenkilön tunnisteen (esimerkiksi loit yhteyshenkilön API:n kautta tai sait sen webhookista). Käytä `POST /contacts/{contactId}/send-message`.
- **Lähetä yhteyshenkilön identiteetillä** – tiedät yhteyshenkilön puhelinnumeron, Instagram-tunnisteen jne., mutta et heidän sisäistä tunnistettaan. Käytä `POST /contacts/send` ja anna alustan etsiä oikea yhteyshenkilö.

Molemmat asettavat viestin jonoon samalla tavalla ja toimittavat sen kanavalla, jota yhteyshenkilö käyttää. Sinun ei tarvitse valita siirtotapaa – alusta reitittää WhatsApp-yhteyshenkilöt WhatsAppin kautta, SMS-yhteyshenkilöt tekstiviestillä ja niin edelleen.

### Lähetä yhteyshenkilön tunnisteella

`POST /contacts/{contactId}/send-message`

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `body` | Kyllä | Lähetettävä viestiteksti. |
| `mediaUrl` | Ei | Liitettävän mediatiedoston (kuva, asiakirja jne.) URL-osoite. |
| `mediaContentType` | Ei | Liitetyn median MIME-tyyppi, esim. `image/jpeg`. |
| `pauseBot` | Ei | `true` keskeyttää tekoälyn tälle yhteyshenkilölle viestin lähetyksen yhteydessä – tarkoitettu ihmisen suorittamaa haltuunottoa varten. Katso [Tekoälyn keskeyttäminen tai jatkaminen](#pause-or-resume-the-ai-for-one-contact). |
| `clearIncompleteReply` | Ei | `true` hylkää keskeneräisen botin vastauksen, jotta se ei jatku viestisi jälkeen. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/send-message" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi! Your appointment is confirmed for tomorrow at 10:00."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/send-message",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      body: "Hi! Your appointment is confirmed for tomorrow at 10:00.",
    }),
  }
);
const data = await res.json();
console.log(data.messageId);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/send-message",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"body": "Hi! Your appointment is confirmed for tomorrow at 10:00."},
)
print(res.json()["messageId"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "messageId": "aB3dE5fG7hI9jK1lM2nO",
  "contactId": "contact123",
  "channel": "whatsapp",
  "message": "Message created successfully. Delivery is being processed."
}
```

### Lähetä yhteyshenkilön identiteetillä

`POST /contacts/send`

Käytä tätä, kun sinulla ei ole yhteyshenkilön sisäistä tunnistetta. Anna viestin `body` sekä **joko** `contact_id` **tai** `channel` yhdessä kyseistä kanavaa vastaavan identiteettikentän kanssa.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `body` | Kyllä | Lähetettävä viestiteksti. |
| `contact_id` | Ei | Olemassa olevan yhteyshenkilön tunnus. Kun tämä on asetettu, alla olevia identiteettikenttiä ei tarvita. |
| `channel` | Ei | Kanava, jota käytetään lähetykseen. Pakollinen, kun `contact_id` ei ole määritetty. Yksi 14:stä lähtevään viestintään soveltuvasta kanavasta: `whatsapp`, `whatsapp_web`, `sms`, `instagram`, `instagram_private`, `messenger`, `telegram`, `chat-widget`, `custom`, `email`, `line`, `imessage`, `linkedin`, `viber`. |
| `phone_number` | Ei | Yhteyshenkilön puhelinnumero kansainvälisessä muodossa. Käytetään yhdessä `whatsapp`, `whatsapp_web` ja `sms` kanssa. |
| `instagram_id` | Ei | Yhteyshenkilön Instagram-käyttäjätunnus. Käytetään `instagram` kanssa. |
| `messenger_id` | Ei | Yhteyshenkilön Messenger-käyttäjätunnus. Käytetään `messenger` kanssa. |
| `telegram_user_id` | Ei | Yhteyshenkilön Telegram-käyttäjätunnus. Käytetään `telegram` kanssa. |
| `media_url` | Ei | Liitettävän mediatiedoston URL-osoite. |
| `media_content_type` | Ei | Liitetyn median MIME-tyyppi, esim. `image/jpeg`. |

**Mitkä kanavat voidaan tunnistaa identiteetin perusteella.** Vain kuusi 14 kanavasta hyväksyy identiteettikentän `contact_id` sijasta: `whatsapp`, `whatsapp_web` ja `sms` haetaan `phone_number` perusteella, `instagram` haetaan `instagram_id` perusteella, `messenger` haetaan `messenger_id` perusteella ja `telegram` haetaan `telegram_user_id` perusteella. Muilla kahdeksalla — `instagram_private`, `chat-widget`, `custom`, `email`, `line`, `imessage`, `linkedin` ja `viber` — ei ole julkista identiteettiä, jota voisi hakea, joten lähettäminen näillä kanavilla vaatii `contact_id`; pelkän `channel` välittäminen palauttaa `400`, joka ilmoittaa, että `contact_id` vaaditaan.

**cURL** (käyttäen `?apiKey=`-kyselylomaketta)

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/send?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "whatsapp",
    "phone_number": "+31612345678",
    "body": "Hi! Your appointment is confirmed."
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/contacts/send", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    channel: "whatsapp",
    phone_number: "+31612345678",
    body: "Hi! Your appointment is confirmed.",
  }),
});
const data = await res.json();
console.log(data.message_id, data.channel);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/send",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "channel": "whatsapp",
        "phone_number": "+31612345678",
        "body": "Hi! Your appointment is confirmed.",
    },
)
data = res.json()
print(data["message_id"], data["channel"])
```

**Vastaus** (`201 Created`):

```json
{
  "success": true,
  "message_id": "aB3dE5fG7hI9jK1lM2nO",
  "contact_id": "contact123",
  "channel": "whatsapp"
}
```

> **Miksi viesti saatetaan hylätä:** Yhteyshenkilö, jolla on käytössä ”älä häiritse” -tila tai yksityinen tila, ei voi vastaanottaa lähteviä viestejä — pyyntö epäonnistuu ja palauttaa virheen `422`. Jos mikään yhteyshenkilö ei vastaa antamaasi tunnusta tai identiteettiä, saat virheen `404`.

---

## Listaa yhteyshenkilön viestit

`GET /contacts/{contactId}/messages`

Palauttaa yhteyshenkilön viestit uusimmasta alkaen, käyttäen osoittimeen (cursor) perustuvaa sivutusta.

| Kyselyparametri | Pakollinen | Kuvaus |
|---|---|---|
| `limit` | Ei | Sivun koko. Oletus `50`, enimmäismäärä `100`. |
| `cursor` | Ei | `next_cursor`-arvo edellisestä vastauksesta. Palauttaa osoitinta vanhemmat viestit. |
| `filter` | Ei | Suodata sisältötyypin mukaan: `all` (oletus), `text`, `media` tai `tool_use`. |
| `direction` | Ei | Suodata suunnan mukaan: `all` (oletus), `inbound` (vastaanotettu yhteyshenkilöltä) tai `outbound` (lähetetty sinun toimestasi). |

> **Huomautus suodatuksesta ja sivutuksesta:** `filter`- ja `direction`-suodattimet sovelletaan jokaiselle sivulle sen lukemisen jälkeen, joten suodatettu sivu voi sisältää vähemmän kohteita kuin `limit`. `next_cursor` etenee edelleen koko keskustelun läpi, joten jatka sivutusta, kunnes `next_cursor` on `null`.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/contacts/contact123/messages?limit=50&direction=inbound" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ limit: "50", direction: "inbound" });
const res = await fetch(
  `https://api.youraiconnector.com/v1/contacts/contact123/messages?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.messages, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"limit": 50, "direction": "inbound"},
)
data = res.json()
print(data["messages"], data["next_cursor"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "messages": [
    {
      "id": "aB3dE5fG7hI9jK1lM2nO",
      "body": "Hi! Thanks for reaching out.",
      "direction": "inbound",
      "channel": "whatsapp",
      "status": "delivered",
      "type": null,
      "timestamp": "2026-06-01T10:00:00.000Z",
      "media_url": null,
      "media_content_type": null,
      "bot_reply": false
    }
  ],
  "next_cursor": "cD4eF6gH8iJ0kL2mN3oP"
}
```

### Viestikentät

| Kenttä | Kuvaus |
|---|---|
| `id` | Viestin yksilöllinen tunniste. |
| `body` | Viestin tekstisisältö. |
| `direction` | `inbound` (vastaanotettu yhteyshenkilöltä) tai `outbound` (tilisi lähettämä). |
| `channel` | Kanava, jota pitkin viesti lähetettiin tai vastaanotettiin (esim. `whatsapp`, `sms`, `instagram`). |
| `status` | Nykyinen toimitustila, esim. `Created`, `sent`, `delivered`, `read`, `failed`. |
| `type` | Viestin tyyppi. Tavallisilla tekstiviesteillä on tyyppi `null`; automatisoidun avustajan työkalutoiminnot on merkitty `tool_use`. |
| `timestamp` | Viestin luontiaika ISO 8601 -muodossa. |
| `media_url` | Liitetyn mediatiedoston URL-osoite, jos sellainen on. |
| `media_content_type` | Liitetyn median MIME-tyyppi, jos sellainen on. |
| `bot_reply` | `true`, kun viesti on tekoälyavustajan luoma. |
| `score` | Arviosi viestistä: `1` peukalo ylös, `-1` peukalo alas, `0` kun viestiä ei ole arvosteltu. Katso [Arvioi tai merkitse viesti tähdellä](#rate-or-star-a-message). |
| `is_important` | `true`, kun viesti on merkitty tähdellä. |
| `is_deleted` | `true`, kun viesti on poistettu. Poistetut viestit pysyvät luettelossa, mutta niiden `body` ja `media_url` ovat tyhjiä. |
| `reactions` | Emojireaktiot viestiin molemmilta osapuolilta. Aina taulukko – tyhjä, jos reaktioita ei ole. Jokaisella merkinnällä on `emoji`, `from_phone_number`, `from_me` (`true` kun reaktio on omasi) ja `reacted_at`. |

---

## Listaa keskusteluistunnot

Keskusteluistunto on yksi keskusteluikkuna yhteyshenkilön kanssa: se avautuu, kun he aloittavat keskustelun, ja sulkeutuu, kun keskustelu päättyy. Istuntojen avulla voit jakaa pitkän historian luettaviin keskusteluihin yhden loputtoman luettelon sijaan.

### Viimeisimmät istunnot kaikilla yhteyshenkilöillä

`GET /chat-sessions/recent`

Palauttaa istunnot, jotka alkoivat viimeisen X tunnin aikana, uusimmasta alkaen, kaikilta tilin yhteyshenkilöiltä.

| Kyselyparametri | Pakollinen | Kuvaus |
|---|---|---|
| `hours` | Kyllä | Kuinka monta tuntia taaksepäin tarkastellaan. On oltava positiivinen kokonaisluku. |
| `status` | Ei | Palauta vain istunnot, joilla on tämä tila: `ChatSessionOpened` tai `ChatSessionClosed`. |
| `limit` | Ei | Palautettavien istuntojen enimmäismäärä. Oletus `100`, enimmäismäärä `100`. |
| `includeMessages` | Ei | `true` lisää `messages`-taulukon jokaiseen istuntoon. Pois päältä oletuksena, koska se tekee vastauksesta huomattavasti suuremman. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-sessions/recent?hours=24&status=ChatSessionClosed" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ hours: "24", status: "ChatSessionClosed" });
const res = await fetch(`https://api.youraiconnector.com/v1/chat-sessions/recent?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.data.total_sessions, data.data.sessions);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/chat-sessions/recent",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"hours": 24, "status": "ChatSessionClosed"},
)
data = res.json()["data"]
print(data["total_sessions"], data["sessions"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "hours_ago": 24,
    "total_sessions": 2,
    "sessions": [
      {
        "session_id": "session456",
        "contact_id": "contact123",
        "contact_name": "Jane Doe",
        "contact_phone": "+31612345678",
        "contact_email": "jane@example.com",
        "start_date_time": "2026-06-01T09:55:00.000Z",
        "end_date_time": "2026-06-01T10:20:00.000Z",
        "status": "ChatSessionClosed",
        "tag": "Booking enquiry"
      }
    ]
  }
}
```

### Kaikki yhden yhteyshenkilön istunnot

`GET /chat-sessions/{contactId}`

Palauttaa jokaisen keskusteluistunnon yhdelle yhteyshenkilölle. Samat `status`-, `limit`- ja `includeMessages`-parametrit kuin yllä – `hours` ei päde tässä.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-sessions/contact123?limit=20" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "contact_id": "contact123",
    "contact_name": "Jane Doe",
    "total_sessions": 2,
    "sessions": [
      {
        "id": "session456",
        "start_date_time": "2026-06-01T09:55:00.000Z",
        "end_date_time": "2026-06-01T10:20:00.000Z",
        "status": "ChatSessionClosed",
        "tag": "Booking enquiry"
      }
    ]
  }
}
```

> **Istunnon tunnisteen kenttien nimet eroavat näiden kahden päätepisteen välillä.** Viimeisimpien istuntojen luettelossa sitä kutsutaan nimellä `session_id` (se sisältää myös yhteyshenkilön tiedot, koska istunnot tulevat monilta yhteyshenkilöiltä); yhteyshenkilökohtaisessa luettelossa sitä kutsutaan nimellä `id`. Kumpaa tahansa arvoa käytetään, kun haet koko keskusteluketjua alla olevan `{sessionId}`-kohdan kautta.

Kun `includeMessages=true`, jokainen istunto saa `messages`-taulukon, jonka merkinnät sisältävät `id`, `body`, `direction`, `timestamp`, `type`, `channel` ja `status`.

---

## Hae keskusteluketju

`GET /contacts/{contactId}/chat-sessions/{sessionId}/messages`

Chat-istunto ryhmittelee yhteyshenkilön viestit yhteen keskusteluikkunaan. Tämä päätepiste palauttaa yksittäisen istunnon koko viestiketjun **vanhimmasta alkaen** sekä istunnon metatiedot. Löydät yhteyshenkilön istuntotunnukset chat-istuntojen päätepisteiden kautta.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.session, data.messages);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["session"], data["messages"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "session": {
    "id": "session456",
    "status": "ChatSessionClosed",
    "start_date_time": "2026-06-01T09:55:00.000Z",
    "end_date_time": "2026-06-01T10:20:00.000Z",
    "tag": "Booking enquiry"
  },
  "messages": [
    {
      "id": "aB3dE5fG7hI9jK1lM2nO",
      "body": "Hi! Thanks for reaching out.",
      "direction": "inbound",
      "channel": "whatsapp",
      "status": "delivered",
      "type": null,
      "timestamp": "2026-06-01T09:55:00.000Z",
      "media_url": null,
      "media_content_type": null,
      "bot_reply": false
    }
  ]
}
```

`session`-objekti raportoi `status` (`ChatSessionOpened` aktiivisena, `ChatSessionClosed` kun päättynyt), `start_date_time`, `end_date_time` ja ihmisluettavan `tag`. `messages`-taulukko käyttää samoja [viestikenttiä](#message-fields) kuin listauspäätepiste.

---

## Muokkaa, poista ja reagoi viesteihin

Nämä päätepisteet muuttavat viestiä sen lähettämisen jälkeen. Kaksi niistä ottaa yhteyttä yhteyshenkilön kanavaan sekä omaan kopioosi, joten lue osion johdanto ennen niiden käyttöönottoa – se, mikä on mahdollista, riippuu täysin kanavasta, jossa keskustelu käydään.

**Mitä kukin kanava sallii**

| Toiminto | Kanavat, jotka voivat muuttaa yhteyshenkilön kopiota | Aikaraja |
|---|---|---|
| Lähetetyn viestin muokkaaminen | Chat-widget, WhatsApp Web, Telegram, LinkedIn | Ei aikarajaa chat-widgetissä, 15 minuuttia WhatsApp Webissä, 48 tuntia Telegramissa, 60 minuuttia LinkedInissä |
| Poistaminen kaikilta | Chat-widget, WhatsApp Web, Telegram, LinkedIn | 60 minuuttia LinkedInissä; muilla ei ole julkaistua aikarajaa |
| Reagoiminen emojilla | WhatsApp Web, Telegram | Ei ole |

Kaikissa muissa kanavissa — WhatsApp Business API, SMS, Instagram, Messenger, sähköposti, LINE, mukautetut kanavat — poistaminen poistaa viestin edelleen postilaatikostasi, mutta yhteyshenkilö säilyttää oman kopionsa, eikä muokkaaminen tai reagoiminen ole lainkaan mahdollista.

### Viestin muokkaaminen

`POST /contacts/{contactId}/messages/{messageId}/edit`

Kirjoittaa uudelleen jo lähettämäsi viestin sekä yhteyshenkilön laitteella että omassa kopiossasi.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `body` | Kyllä | Uusi viestiteksti. Ei saa olla tyhjä ja voi olla enintään 4096 merkkiä pitkä. |

Toisin kuin poistaminen, tämä **epäonnistuu näkyvästi**, jos kanava kieltäytyy: saat `409`-vastauksen ja oma kopiosi jää täsmälleen sellaiseksi kuin se on yhteyshenkilöllä, koska sellaisen muokkauksen näyttäminen, jota he eivät koskaan vastaanottaneet, saisi osapuolet epäsynkroniin. `edit_reason`-kenttä kertoo syyn — kanavan muokkausaika on umpeutunut, kanava on katkaistu tai jokin muu meni vikaan.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Sorry - I meant Thursday at 3pm." }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ body: "Sorry - I meant Thursday at 3pm." }),
  }
);
const data = await res.json();
console.log(data.edited, data.edit_reason);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"body": "Sorry - I meant Thursday at 3pm."},
)
data = res.json()
print(data.get("edited"), data.get("edit_reason"))
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "edited": true,
  "edit_reason": "edit_dispatched"
}
```

Jos kanava ei hyväksy muokkausta, saat sen sijaan `409`-vastauksen, eikä mitään muutettu:

```json
{
  "success": false,
  "error": "The message could not be edited",
  "edit_reason": "channel_disconnected"
}
```

Jo poistettu viesti, kanava, joka ei tue muokkaamista lainkaan, ja viesti, joka on liian vanha kanavalleen, palauttavat kaikki `400`-vastauksen — pyyntö ei koskaan saavuta kanavaa.

### Yhden viestin poistaminen

`DELETE /contacts/{contactId}/messages/{messageId}`

Poistaa viestin keskustelustasi ja, jos kanava sen sallii, hakee myös yhteyshenkilön kopion takaisin. Ei pyyntörunkoa.

Tämä vastaa aina `200`, kun viesti oli olemassa, vaikka yhteyshenkilön kopiota ei olisi voitu poistaa — oma kopiosi **on** poistettu, joten virheilmoitus olisi harhaanjohtava. Lue vastauksen kolme kenttää kertoaksesi käyttäjälle, mitä todellisuudessa tapahtui:

| Kenttä | Kuvaus |
|---|---|
| `revoke_supported` | Voiko tämä kanava poistaa viestejä lainkaan. |
| `revoked` | Poistettiinko kopio yhteyshenkilön laitteelta. |
| `revoke_reason` | Miksi sitä ei poistettu, kun `revoked` on `false` — esimerkiksi `revoke_window_closed` tai `already_deleted`. |

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.revoked, data.revoke_reason);
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["revoked"], data["revoke_reason"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "revoke_supported": true,
  "revoked": true,
  "revoke_reason": "revoke_dispatched"
}
```

> Poistettuja viestejä ei poisteta keskusteluhistoriasta. Ne säilyvät kohdassa `GET /contacts/{contactId}/messages`, jossa on `is_deleted: true` sekä tyhjä `body` ja `media_url`.

### Poista useita viestejä kerralla

`POST /contacts/{contactId}/messages/bulk-delete`

Tyhjentää viestierän vain omalta puoleltasi. Viestien sisältö ja liitteet tyhjennetään, mutta **mitään ei peruta yhteyshenkilön laitteelta** – jos haluat myös peruuttaa viestin, poista se yksitellen yllä olevan yksittäisen viestin päätepisteen kautta.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `message_ids` | Kyllä | Tyhjästä poikkeava taulukko viestien tunnisteita, enintään 500 per pyyntö. `messageIds` hyväksytään aliaksena. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message_ids": ["msg_1", "msg_2"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ message_ids: ["msg_1", "msg_2"] }),
  }
);
console.log((await res.json()).deleted);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"message_ids": ["msg_1", "msg_2"]},
)
print(res.json()["deleted"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "deleted": 2
}
```

### Reagoi viestiin

`POST /contacts/{contactId}/messages/{messageId}/react`

Lisää oman emojireaktiosi viestiin tai poista se lähettämällä tyhjä merkkijono. Yhteyshenkilön omia reaktioita ei koskaan muuteta.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `emoji` | Kyllä | Emojireaktio tai `""` reaktion poistamiseksi. Täytyy olla yksittäinen merkkijono ilman välilyöntejä, enintään 16 merkkiä. |

Muokkaamisen tavoin tämä epäonnistuu sen sijaan, että näyttäisi reaktion, jota yhteyshenkilö ei koskaan saanut, ja virheilmoitus kertoo, kannattaako yrittää uudelleen:

- `422` — viestiä ei voi koskaan toimittaa tässä keskustelussa: kanava ei tue reaktioita, viestillä ei ole kanavapuolen tunnistetta tai emoji ei kuulu kanavan sallimiin merkkeihin.
- `409` — kanava ei ollut hetkellisesti tavoitettavissa. Uudelleenyritys saattaa toimia.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "emoji": "👍" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ emoji: "👍" }),
  }
);
const data = await res.json();
console.log(data.reactions);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"emoji": "👍"},
)
print(res.json()["reactions"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "reaction_supported": true,
  "reaction_reason": "reaction_dispatched",
  "reactions": [
    {
      "emoji": "👍",
      "from_phone_number": "+31612345678",
      "from_me": true,
      "reacted_at": "2026-06-01T10:05:00.000Z"
    }
  ]
}
```

`reactions`-taulukko sisältää kaikki viestin reaktiot, sekä sinun että yhteyshenkilön. `409`- tai `422`-tilanteessa se palautetaan muuttumattomana, joten suoraan siitä renderöivä asiakasohjelma ei koskaan näytä reaktiota, jota ei ole toimitettu.

### Arvioi tai tähditä viesti

`PATCH /contacts/{contactId}/messages/{messageId}`

Arvioi viestin peukalolla ylös tai alas ja/tai merkitse se tähdellä tärkeäksi. Tämä on kirjanpitoa vain omalla puolellasi – mitään ei lähetetä yhteyshenkilölle.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `score` | Ei | `1` peukku ylös, `-1` peukku alas, `0` poistaa arvion. |
| `is_important` | Ei | `true` lisää tähden viestiin, `false` poistaa sen. On oltava totuusarvo (boolean), ei merkkijono `"true"`. |

Lähetä vähintään toinen näistä, muuten saat `400`-virheen. Vain lähetetyt tiedot kirjoitetaan, joten viestin tähdittäminen ei koskaan poista sen arviota ja päinvastoin – ja vastaus palauttaa vain lähettämäsi kentät.

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "score": 1, "is_important": true }'
```

**JavaScript**

```javascript
await fetch("https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1", {
  method: "PATCH",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ score: 1, is_important: true }),
});
```

**Python**

```python
import requests

requests.patch(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"score": 1, "is_important": True},
)
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "score": 1,
  "is_important": true
}
```

---

## Merkitse viestit luetuiksi

Voit poistaa lukemattoman tilan joko tietyiltä viesteiltä tai koko keskustelulta.

### Merkitse tietyt viestit luetuiksi

`POST /contacts/{contactId}/messages/mark-read`

Välitä niiden viestien tunnukset, jotka haluat merkitä luetuiksi.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `message_ids` | Kyllä | Tyhjentämätön taulukko viestien tunnuksista (enintään 500 per pyyntö). |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message_ids": ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      message_ids: ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"],
    }),
  }
);
const data = await res.json();
console.log(data.marked_read);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"message_ids": ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"]},
)
print(res.json()["marked_read"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123",
  "marked_read": 2
}
```

### Merkitse koko chat luetuksi

`POST /contacts/{contactId}/mark-read`

Poistaa lukemattomien viestien merkin yhteyshenkilön koko keskustelusta saapuneet-kansiossa. Pyyntörunkoa ei tarvita.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/mark-read" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/mark-read",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/mark-read",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

### Merkitse koko keskustelu lukemattomaksi

`POST /contacts/{contactId}/mark-unread`

Palauttaa lukemattoman viestin merkin keskusteluun – kätevää, kun joku tiimistäsi on avannut keskustelun, mutta siirtää sen takaisin. Pyynnön runkoa ei tarvita.

Tämä on vain postilaatikkoa koskeva lippu: se **ei** muuta sitä, milloin keskustelu on viimeksi luettu, joten lukukuittausta ei lähetetä yhteyshenkilölle kanavilla, jotka tukevat niitä.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/mark-unread" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

---

## Vie keskustelu

Viennit tarjoavat koko keskustelun luettavana litteraattina sen sijaan, että joutuisit selaamaan viestejä sivuittain. Jokainen vientirajapinta hyväksyy `filter`-parametrin, jonka arvo on `all` (oletus), `text`, `media` tai `tool_use`, vastaten viestilistan suodatinta.

### Vie yhden yhteyshenkilön keskustelu

`GET /chat-exports/{contactId}`

| Kyselyparametri | Pakollinen | Kuvaus |
|---|---|---|
| `format` | Ei | `txt` (oletus) palauttaa latauslinkin selväkieliseen litteraattiin. `json` palauttaa viestit jäsenneltynä tietona vastauksessa. |
| `filter` | Ei | `all` (oletus), `text`, `media` tai `tool_use`. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-exports/contact123?format=json" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/chat-exports/contact123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"format": "json"},
)
print(res.json()["data"]["messages"])
```

**Vastaus, jossa on `format=json`** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "contact": {
      "id": "contact123",
      "name": "Jane Doe",
      "phone": "+31612345678",
      "email": "jane@example.com"
    },
    "messages": [
      {
        "body": "Hi! I have a question about my order.",
        "direction": "inbound",
        "timestamp": "2026-06-01T09:55:00.000Z",
        "type": "text",
        "media_url": null,
        "media_content_type": null,
        "name": null,
        "args": null
      }
    ]
  }
}
```

Kun käytössä on `format=txt` (oletus), `data` on sen sijaan latauslinkki luotuun litteraattitiedostoon:

```json
{
  "success": true,
  "data": "https://storage.googleapis.com/.../chat-export-contact123-....txt"
}
```

> **Latauslinkki on lyhytikäinen.** Hae tiedosto heti, kun saat linkin, sen sijaan että tallentaisit sen – pyydä uusi vienti, kun tarvitset litteraattia uudelleen.

### Vie kaikki viimeaikaiset keskustelut

`GET /chat-exports/recent`

Vie kaikkien niiden yhteystietojen keskustelut, jotka ovat olleet aktiivisia viimeisen X tunnin aikana, yhdellä kutsulla.

| Kyselyparametri | Pakollinen | Kuvaus |
|---|---|---|
| `hours` | Kyllä | Kuinka monen tunnin ajalta aktiivisuutta tarkastellaan. On oltava positiivinen kokonaisluku. |
| `format` | Ei | `json` (oletus) palauttaa yhden merkinnän per yhteystieto. `txt` palauttaa yhden ladattavan tekstitiedoston, joka sisältää kaikki keskustelut. |
| `limit` | Ei | Vientiin sisällytettävien yhteystietojen enimmäismäärä. Oletus `50`, enimmäismäärä `100`. |
| `filter` | Ei | `all` (oletus), `text`, `media` tai `tool_use`. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-exports/recent?hours=24&limit=25" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "hours_ago": 24,
    "total_contacts": 2,
    "exports": [
      {
        "contactId": "contact123",
        "contactName": "Jane Doe",
        "phoneNumber": "+31612345678",
        "email": "jane@example.com",
        "messageCount": 12,
        "chatExport": "Acme Export - Jane Doe\nPhone: +31612345678\n..."
      }
    ]
  }
}
```

Kun käytössä on `format=txt`, vastaus on itse tekstitiedosto, joka lähetetään ladattavana tiedostona JSON-muodon sijaan.

> Tämä yksi kutsu hakee kaikkien vastaavien yhteystietojen koko historian, joten pidä `hours` ja `limit` maltillisina kiireisillä tileillä.

### Lähetä keskusteluhistoria sähköpostitse yhteystiedolle

`POST /chat-exports/{contactId}/email`

Lähettää yhteystiedolle hänen oman keskusteluhistoriansa sähköpostitse – tämä on "lähetä tämä keskustelu minulle sähköpostilla" -toiminto, joka käynnistetään omasta järjestelmästäsi.

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `recipient_email` | Ei | Minne se lähetetään. Oletuksena yhteystiedon tallennettu sähköpostiosoite. |
| `via` | Ei | `auto` (oletus) valitsee parhaan reitin, `transactional` lähettää sen järjestelmäsähköpostina, `email_channel` lähettää sen yhdistetyn sähköpostikanavasi kautta. |
| `note` | Ei | Lyhyt viesti sinulta, joka näkyy keskusteluhistorian yläpuolella. Enintään 1000 merkkiä. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/chat-exports/contact123/email" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "note": "Here is a copy of our chat, as promised." }'
```

**Vastaus** (`200 OK`):

```json
{
  "success": true,
  "data": {
    "via": "transactional",
    "recipientEmail": "jane@example.com",
    "messageCount": 42,
    "omittedCount": 0
  }
}
```

`omittedCount` kertoo, kuinka monta vanhinta viestiä jätettiin pois, jotta sähköpostin pituus pysyisi järkevänä. `200` tarkoittaa, että keskusteluhistoria luotiin ja asetettiin lähetysjonoon, ei sitä, että se olisi jo saapunut postilaatikkoon.

---

## Tekoälyn keskeyttäminen tai jatkaminen yhdelle yhteyshenkilölle

`PUT /contacts/{contactId}`

Aseta `is_bot_active` arvoon `false`, kun haluat estää tekoälyä vastaamasta yhdelle yhteyshenkilölle, ja takaisin arvoon `true`, kun haluat palauttaa keskustelun botille. Tämä on haltuunottokytkin, jota tarvitset, kun ihminen astuu keskusteluun: API:n kautta lähettämäsi viestit toimitetaan edelleen, vaikka botti olisi keskeytettynä.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/contacts/contact123" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_bot_active": false }'
```

**JavaScript**

```javascript
await fetch("https://api.youraiconnector.com/v1/contacts/contact123", {
  method: "PUT",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ is_bot_active: false }),
});
```

**Python**

```python
import requests

requests.put(
    "https://api.youraiconnector.com/v1/contacts/contact123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"is_bot_active": False},
)
```

**Vastaus**

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

**Keskeyttäminen vastauksen yhteydessä**

Jos ihminen ottaa keskustelun haltuunsa lähettämällä vastauksen, voit keskeyttää botin samassa pyynnössä sen sijaan, että tekisit toisen kutsun. `POST /contacts/{contactId}/send-message` hyväksyy kaksi valinnaista lippua:

| Kenttä | Kuvaus |
|---|---|
| `pauseBot` | `true` keskeyttää tekoälyn tälle yhteyshenkilölle viestin lähetyksen yhteydessä. |
| `clearIncompleteReply` | `true` hylkää keskeneräisen botin vastauksen, jotta se ei jatku sen jälkeen. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/send-message" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi, Sarah here - taking over from the assistant.",
    "pauseBot": true,
    "clearIncompleteReply": true
  }'
```

Vastaus sisältää kohdan `"botPaused": true`, kun keskeytys on otettu käyttöön.

> Yhteyshenkilön merkitseminen yksityiseksi käyttämällä kohdetta [`POST /contacts/bulk-flag`](contacts.md) keskeyttää myös botin kyseisen henkilön kohdalla. Katso täydellinen kenttäluettelo kohdasta [Yhteyshenkilöt](contacts.md).

---

## Oman postilaatikon rakentaminen

Kaikki postilaatikon tarvitsema tieto löytyy tältä sivulta ja kohdasta [Yhteyshenkilöt](contacts.md):

| Mitä tarvitset | Päätepiste |
|---|---|
| Listaa keskustelut | `GET /contacts` |
| Lue keskustelu | `GET /contacts/{contactId}/messages` |
| Listaa yhteystiedon chat-istunnot | `GET /chat-sessions/{contactId}` |
| Katso mitä on tullut hiljattain | `GET /chat-sessions/recent` |
| Lue yksi chat-istunto | `GET /contacts/{contactId}/chat-sessions/{sessionId}/messages` |
| Lähetä manuaalinen vastaus | `POST /contacts/{contactId}/send-message` |
| Korjaa juuri lähettämäsi vastaus | `POST /contacts/{contactId}/messages/{messageId}/edit` |
| Poista viesti | `DELETE /contacts/{contactId}/messages/{messageId}` |
| Tyhjennä useita viestejä | `POST /contacts/{contactId}/messages/bulk-delete` |
| Reagoi emojilla | `POST /contacts/{contactId}/messages/{messageId}/react` |
| Arvioi tai tähditä viesti | `PATCH /contacts/{contactId}/messages/{messageId}` |
| Merkitse luetuksi | `POST /contacts/{contactId}/mark-read` |
| Palauta chat tiimille | `POST /contacts/{contactId}/mark-unread` |
| Vie keskusteluhistoria | `GET /chat-exports/{contactId}` |
| Keskeytä tai jatka tekoälyä | `PUT /contacts/{contactId}` käyttäen `is_bot_active` |

Reaaliaikaisia päivityksiä varten tilaa tapahtumat `New Message`, `Replies`, `Human Alerted` ja `Chat Concluded` käyttämällä [Webhookeja](webhooks.md) sen sijaan, että kyselisit tätä API:a ajastetusti.

---

## Messages API -virheet

Viestien päätepisteet palauttavat vakiomuotoisen virhekuoren:

```json
{
  "success": false,
  "error": "Contact not found"
}
```

| Tila | Milloin se tapahtuu viestin päätepisteessä |
|---|---|
| `400` | Pakollinen kenttä puuttuu tai parametri on virheellinen (väärä `limit`, `hours`, `filter`, `direction`, `status`, tyhjä tai yli 500 viestin `message_ids`-taulukko, virheellinen `cursor`, tyhjä tai liian pitkä muokkaus `body`, `score` `-1`/`0`/`1`-alueen ulkopuolella, tai emoji, jossa on välilyöntejä tai joka on yli 16 merkkiä pitkä). Palautetaan myös, kun viestiä ei voi muokata lainkaan – se on poistettu, sen kanava ei tue muokkausta tai se on kyseisen kanavan muokkausajan ulkopuolella. |
| `404` | Yhteystietoa, chat-istuntoa tai yhtä annetuista viestitunnuksista ei löytynyt. |
| `409` | Kanava ei hyväksy muutosta juuri nyt. Mitään ei kirjoitettu: muokkauksen yhteydessä `edit_reason` kertoo syyn; reaktion yhteydessä kanava oli hetkellisesti tavoittamattomissa ja uusi yritys voi toimia. |
| `422` | Yhteystieto ei voi vastaanottaa lähteviä viestejä (älä häiritse -tila, yksityinen tai tukematon kanava), tai reaktiota ei voida koskaan toimittaa tässä keskustelussa (`reaction_reason` kertoo kumpi). |

Jaetut koodit, joita jokainen päätepiste voi palauttaa — `401`, `403` (tilauksesi ei sisällä API-käyttöoikeutta), `429` (nopeusrajoitus) ja `500` — on lueteltu uudelleenyritysohjeiden kera kohdassa [Virheet ja sivutus](errors-and-pagination.md).

---

## Seuraavat vaiheet

- [Webhooks](webhooks.md) — vastaanota toimitustilan päivitykset suoraan sen sijaan, että kyselisit niitä.
- [Yhteystiedot](contacts.md) — luo ja etsi yhteystietoja, joille lähetät viestejä.
- [Ajanvaraukset](appointments.md) — varaa ja hallinnoi yhteystietojesi ajanvarauksia.
