
# UKK-rajapinta

UKK-osiot ovat kysymys-vastaus-pareja, joita tekoälybottisi käyttää vastatessaan asiakkaille. Jokainen UKK kuuluu tilillesi, ja se voidaan linkittää yhteen tai useampaan kampanjaan, jolloin samaa vastausta voidaan käyttää uudelleen kaikkialla, missä se on tarkoituksenmukaista. UKK-rajapinnan avulla voit hallita tätä kirjastoa ohjelmallisesti – luoda, päivittää, tuoda massana, järjestää uudelleen ja linkittää UKK-osioita kampanjoihin omasta koodistasi käsin.

Kaikki alla olevat päätepisteet ovat suhteessa perus-URL-osoitteeseen `https://api.youraiconnector.com/v1`. Jokainen pyyntö on todennettava – katso [API-käyttöoikeus](../integrations/api-access.md) ja [Todennus](authentication.md). API-käyttöoikeus on maksullinen ominaisuus; ilman sitä pyynnöt hylätään virheellä `403`.

> **Miten botti käyttää UKK-osiota:** Kun luot tai muutat UKK-osiota, alusta valmistelee sen hakutiedot (joita käytetään UKK-osion täsmäämiseen saapuviin kysymyksiin) taustalla. Tämä valmistuu yleensä muutamassa sekunnissa, minkä jälkeen botti alkaa käyttää merkintää automaattisesti.


---

## UKK-objekti

Jokaisella rajapinnasta palautettavalla UKK-osiolla on tämä rakenne:

| Kenttä | Tyyppi | Kuvaus |
|---|---|---|
| `id` | string | FAQ:n yksilöllinen tunniste. |
| `question` | string | Asiakkaan kysymys, johon tämä vastaus vastaa. |
| `answer` | string | Vastaus, jonka tekoälybotti antaa. |
| `category` | string | null | Valinnainen vapaamuotoinen luokkatunniste. |
| `tags` | string[] | Valinnaiset tunnisteet FAQ-kohtien järjestämiseen. |
| `is_active` | boolean | Saako botti käyttää tätä FAQ-kohtaa. Oletusarvo on `true`. |
| `is_global` | boolean | Merkitsee FAQ-kohdan sellaiseksi, ettei se ole sidottu yhteen tiettyyn kampanjaan tai agenttiin. Tämä ei tarkoita, että FAQ-kohta pätisi kaikkialla: FAQ-kohtaa käyttävät vain ne kampanjat ja agentit, joihin se on linkitetty. Oletusarvo on `false`. |
| `usage_count` | integer | Kuinka monta kertaa tätä FAQ-kohtaa on käytetty tekoälyn vastauksissa. |
| `order_index` | integer | Tämän FAQ-kohdan näyttöjärjestys kampanjan sisällä. |
| `campaign_ids` | string[] | Niiden kampanjoiden tunnisteet, joihin tämä FAQ-kohta on linkitetty. |
| `created_at` | string | null | ISO 8601 -aikaleima FAQ-kohdan luomisajankohdasta. |
| `updated_at` | string | null | ISO 8601 -aikaleima viimeisimmästä muutoksesta. |

Kentät, joita voit **asettaa**, ovat: `question`, `answer`, `is_active`, `is_global`, `category`, `tags` ja `order_index`. Alusta hallitsee kaikkea muuta (hakutiedot, käyttökerrat, aikaleimat); kaikki muut pyynnön rungossa olevat kentät jätetään huomiotta.

---

## Listaa UKK-osiot

`GET /faqs`

Palauttaa tilisi UKK-osiot uusimmasta alkaen. Voit halutessasi suodattaa tulokset yksittäisen kampanjan tai aktiivisuustilan mukaan.

**Kyselyparametrit**

| Parametri | Pakollinen | Kuvaus |
|---|---|---|
| `campaign_id` | Ei | Palauta vain tähän kampanjaan linkitetyt UKK-osiot. |
| `is_active` | Ei | Palauta vain UKK-osiot, joilla on tämä aktiivisuustila (`true` tai `false`). Tämä suodatin käytetään sivukohtaisesti, joten sivu voi sisältää vähemmän kohteita kuin `limit`. |
| `limit` | Ei | UKK-osioiden enimmäismäärä sivua kohden. Oletus `50`, enimmäismäärä `100`. |
| `cursor` | Ei | UKK-tunniste, josta jatketaan. Välitä edellisen sivun `next_cursor`-arvo. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/faqs?campaign_id=campaign123&limit=50&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs?campaign_id=campaign123&limit=50",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.faqs, data.next_cursor);
```

**Python**

```python
import requests

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

**Vastaus**

```json
{
  "success": true,
  "faqs": [
    {
      "id": "aBcD1234eFgH5678",
      "question": "How long does shipping take?",
      "answer": "Standard shipping takes 3-5 business days.",
      "category": "shipping",
      "tags": ["logistics", "delivery"],
      "is_active": true,
      "is_global": false,
      "usage_count": 12,
      "order_index": 0,
      "campaign_ids": ["campaign123"],
      "created_at": "2026-01-01T12:00:00.000Z",
      "updated_at": "2026-01-02T08:30:00.000Z"
    }
  ],
  "next_cursor": "aBcD1234eFgH5678"
}
```

Kun `next_cursor` on `null`, tuloksia ei ole enempää.

---

## Hae UKK

`GET /faqs/{faqId}`

Palauttaa yksittäisen UKK:n sen tunnisteen perusteella.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const { faq } = await res.json();
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
faq = res.json()["faq"]
```

**Vastaus**

```json
{
  "success": true,
  "faq": {
    "id": "aBcD1234eFgH5678",
    "question": "How long does shipping take?",
    "answer": "Standard shipping takes 3-5 business days.",
    "category": "shipping",
    "tags": ["logistics"],
    "is_active": true,
    "is_global": false,
    "usage_count": 12,
    "order_index": 0,
    "campaign_ids": ["campaign123"],
    "created_at": "2026-01-01T12:00:00.000Z",
    "updated_at": "2026-01-02T08:30:00.000Z"
  }
}
```

---

## Luo UKK

`POST /faqs`

Luo uuden UKK:n ja linkittää sen kampanjaan.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `campaign_id` | Kyllä | Kampanja, johon uusi UKK linkitetään. |
| `question` | Kyllä | Asiakkaan kysymys, johon tämä vastaa. |
| `answer` | Kyllä | Vastaus, jonka botin tulisi antaa. |
| `is_active` | Ei | Saako botti käyttää tätä UKK:ta. Oletusarvo on `true`. |
| `is_global` | Ei | Koskeeko UKK kaikkia kampanjoita. Oletusarvo on `false`. |
| `category` | Ei | Vapaamuotoinen luokkatunniste. |
| `tags` | Ei | Taulukko tunnisteita. |
| `order_index` | Ei | Näyttöpaikka kampanjan sisällä. Oletusarvo on `0`. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign123",
    "question": "How long does shipping take?",
    "answer": "Standard shipping takes 3-5 business days.",
    "category": "shipping",
    "tags": ["logistics"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "campaign123",
    question: "How long does shipping take?",
    answer: "Standard shipping takes 3-5 business days.",
    category: "shipping",
    tags: ["logistics"],
  }),
});
const { faq_id } = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign123",
        "question": "How long does shipping take?",
        "answer": "Standard shipping takes 3-5 business days.",
        "category": "shipping",
        "tags": ["logistics"],
    },
)
faq_id = res.json()["faq_id"]
```

**Vastaus**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678"
}
```

---

## Päivitä UKK

`PUT /faqs/{faqId}`

Päivittää UKK:n osittain. Vain annetut kirjoitettavat kentät muuttuvat; kaikki muu säilyttää nykyisen arvonsa. Kentän `question` tai `answer` muuttaminen päivittää UKK:n hakutiedot automaattisesti taustalla.

Jos lähetät `question` tai `answer`, niiden on oltava tyhjiä merkkijonoja. Jos et lähetä yhtään tunnistettua kirjoitettavaa kenttää, palautetaan `400`.

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ is_active: false }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"is_active": False},
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678"
}
```

---

## Poista UKK

`DELETE /faqs/{faqId}`

Poistaa UKK:n pysyvästi. Voit halutessasi välittää `campaign_id`-parametrin kyselyparametrina, jolloin UKK poistetaan myös kyseisen kampanjan UKK-luettelosta.

**Kyselyparametrit**

| Parametri | Pakollinen | Kuvaus |
|---|---|---|
| `campaign_id` | Ei | Poista myös UKK tästä kampanjan UKK-luettelosta. |

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678?campaign_id=campaign123&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678?campaign_id=campaign123",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678",
    params={"campaign_id": "campaign123"},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true
}
```

---

## UKK-osioiden massapoisto

`POST /faqs/bulk-delete`

Poistaa enintään 500 UKK-osiota yhdellä pyynnöllä. Kun `campaign_id` on määritetty, poistetut UKK-osiot poistetaan myös kyseisen kampanjan UKK-luettelosta.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `faq_ids` | Kyllä | Tyhjästä poikkeava taulukko poistettavien UKK-osioiden tunnisteista (enintään 500). |
| `campaign_id` | Ei | Poista myös poistetut UKK-osiot tästä kampanjan UKK-luettelosta. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/bulk-delete?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "faq_ids": ["faqId1", "faqId2"], "campaign_id": "campaign123" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/bulk-delete", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    faq_ids: ["faqId1", "faqId2"],
    campaign_id: "campaign123",
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/bulk-delete",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"faq_ids": ["faqId1", "faqId2"], "campaign_id": "campaign123"},
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "deleted_count": 2
}
```

---

## UKK-tuonti

`POST /faqs/import`

Tuo massana enintään 500 UKK-kysymystä ja linkitä ne kaikki yhteen kampanjaan. Kohteet, joiden `question` vastaa kirjastossasi olevaa olemassa olevaa UKK-kysymystä (kirjainkokoa huomioimatta), **päivittävät** kyseisen UKK-kysymyksen sen sijaan, että loisivat kaksoiskappaleen.

> **Suorituskykyvinkki:** Kaksoiskappaleiden haku skannaa koko UKK-kirjastosi, joten erittäin suuret kirjastot hidastavat tuontia. Suosi harvempia ja suurempia tuonteja monien pienten sijaan.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `campaign_id` | Kyllä | Kampanja, johon kaikki tuodut UKK-kysymykset linkitetään. |
| `faqs` | Kyllä | Tyhjästä poikkeava taulukko UKK-kohteita (enintään 500). Jokaisella kohteella on oltava tyhjästä poikkeava `question` ja `answer`; se voi sisältää myös kentät `is_active`, `is_global`, `category`, `tags` ja `order_index`. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/import?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign123",
    "faqs": [
      { "question": "Do you ship internationally?", "answer": "Yes, we ship to most countries worldwide." },
      { "question": "What is your return policy?", "answer": "You can return any item within 30 days." }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/import", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "campaign123",
    faqs: [
      {
        question: "Do you ship internationally?",
        answer: "Yes, we ship to most countries worldwide.",
      },
      {
        question: "What is your return policy?",
        answer: "You can return any item within 30 days.",
      },
    ],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/import",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign123",
        "faqs": [
            {"question": "Do you ship internationally?", "answer": "Yes, we ship to most countries worldwide."},
            {"question": "What is your return policy?", "answer": "You can return any item within 30 days."},
        ],
    },
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "faq_ids": ["aBcD1234eFgH5678", "iJkL9012mNoP3456"],
  "imported_count": 2
}
```

`faq_ids` ovat luotujen tai päivitettyjen UKK-kysymysten tunnisteet siinä järjestyksessä kuin toimitit ne.

---

## UKK-kysymysten uudelleenjärjestäminen

`POST /faqs/reorder`

Asettaa kampanjan UKK-kysymysten näyttöjärjestyksen. Toimita **täydellinen** luettelo UKK-tunnisteista halutussa järjestyksessä; kunkin UKK-kysymyksen paikka päivitetään vastaamaan sen paikkaa taulukossa.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `campaign_id` | Kyllä | Kampanja, jonka UKK-osioita järjestellään uudelleen. |
| `ordered_faq_ids` | Kyllä | Tyhjentämätön taulukko kaikista kampanjan UKK-tunnisteista halutussa näyttöjärjestyksessä (enintään 500). |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/reorder?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign123",
    "ordered_faq_ids": ["faqId2", "faqId1", "faqId3"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/reorder", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "campaign123",
    ordered_faq_ids: ["faqId2", "faqId1", "faqId3"],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/reorder",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign123",
        "ordered_faq_ids": ["faqId2", "faqId1", "faqId3"],
    },
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true
}
```

Jos kampanjaa tai mitään UKK-tunnisteista ei löydy tililtäsi, pyyntö palauttaa `404 One or more FAQs were not found`.

---

## Linkitä UKK kampanjaan

`POST /faqs/{faqId}/link`

Linkittää olemassa olevan UKK-osion lisäkampanjaan. UKK-osio voi olla jaettu usean kampanjan kesken, joten sama vastaus tarvitsee ylläpitää vain kerran.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `campaign_id` | Kyllä | Kampanja, johon UKK-osio linkitetään. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/link?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "campaign_id": "campaign456" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/link",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ campaign_id: "campaign456" }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/link",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"campaign_id": "campaign456"},
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678",
  "campaign_id": "campaign456"
}
```

---

## Poista FAQ:n linkitys kampanjasta

`POST /faqs/{faqId}/unlink`

Poistaa FAQ:n kampanjasta poistamatta itse FAQ:ta. FAQ säilyy kirjastossasi ja pysyy linkitettynä muihin kampanjoihin.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `campaign_id` | Kyllä | Kampanja, josta FAQ poistetaan. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/unlink?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "campaign_id": "campaign456" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/unlink",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ campaign_id: "campaign456" }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/unlink",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"campaign_id": "campaign456"},
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678",
  "campaign_id": "campaign456"
}
```

---

## Rakenna FAQ:n hakutiedot uudelleen

`POST /faqs/{faqId}/rebuild-embeddings`

Asettaa jonoon tekoälybotin käyttämien hakutietojen (semanttinen haku ja avainsanahaku) uudelleenrakennuksen. Tämä on hyödyllistä, jos FAQ ei näy vastauksissa odotetulla tavalla. Uudelleenrakennus tapahtuu taustalla ja valmistuu yleensä muutamassa sekunnissa; FAQ voidaan tilapäisesti sulkea pois tekoälyn vastauksista uudelleenrakennuksen aikana.

Tämä päätepiste palauttaa `202 Accepted`, koska työ jatkuu vastauksen lähettämisen jälkeen. `status` on aina `"processing"` — hae FAQ uudelleen myöhemmin, jos haluat varmistaa valmistumisen.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/rebuild-embeddings?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/rebuild-embeddings",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/rebuild-embeddings",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "faq_id": "aBcD1234eFgH5678",
  "status": "processing"
}
```

---

## AI-avusteinen FAQ-hallinta

Alla olevat päätepisteet menevät pidemmälle kuin pelkkä CRUD: ne kutsuvat samoja tekoälyavusteisia työkaluja, joita hallintapaneelin FAQ-editori käyttää – ne etsivät kaksoiskappaleita, luovat merkintöjä dokumenteista ja yhdistävät FAQ-kysymyksiä avoimiin tiedonpuute-tehtäviin. Tämän sarjan pyyntöjen rungot käyttävät `camelCase`-kenttien nimiä (`campaignId`, `taskId`, `sourceIds`...), jotka vastaavat sovelluksen omia pyyntörakenteita, eivätkä muualla tällä sivulla käytettyjä `snake_case`-nimiä – kopioi alla olevat esimerkit sen sijaan, että arvaisit kentän nimen.

### Haarukoi FAQ vain kampanjakohtaiseksi kopioksi

`POST /faqs/{faqId}/fork-for-campaign`

Luo uuden FAQ-kysymyksen, joka on kopio olemassa olevasta, rajattuna yhteen kampanjaan, ja linkittää kyseisen kampanjan uudelleen uuteen kopioon alkuperäisen sijasta. Käytä tätä, kun haluat mukauttaa vastauksen yhdelle kampanjalle muuttamatta sitä kaikkialla muualla, missä alkuperäistä FAQ-kysymystä käytetään. Alkuperäinen FAQ säilyy paikallaan – se menettää vain tämän kampanjan linkityksen.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `campaign_id` | Kyllä | Kampanja, johon uusi kopio rajataan ja josta se linkitetään uudelleen alkuperäisestä FAQ-kysymyksestä. |
| `question` | Kyllä | Kysymys uudelle, kampanjakohtaiselle kopiolle. |
| `answer` | Kyllä | Vastaus uudelle, kampanjakohtaiselle kopiolle. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/fork-for-campaign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign456",
    "question": "How long does shipping take to the EU?",
    "answer": "For EU orders, shipping takes 7-10 business days."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/fork-for-campaign",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      campaign_id: "campaign456",
      question: "How long does shipping take to the EU?",
      answer: "For EU orders, shipping takes 7-10 business days.",
    }),
  }
);
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/aBcD1234eFgH5678/fork-for-campaign",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign456",
        "question": "How long does shipping take to the EU?",
        "answer": "For EU orders, shipping takes 7-10 business days.",
    },
)
data = res.json()
```

**Vastaus** — `201 Created`

```json
{
  "success": true,
  "faq_id": "nEwFaQiD9012mNoP",
  "campaign_id": "campaign456",
  "original_faq_id": "aBcD1234eFgH5678"
}
```

### Etsi lähes identtiset FAQ-kysymykset

`POST /faqs/dedupe`

Käynnistää taustatyön, joka skannaa FAQ-kirjastosi lähes identtisten ja päällekkäisten merkintöjen varalta ja yhdistää tai poistaa ne, kun se on varma asiasta. Hyödyllinen massatuonnin jälkeen tai kun useat tekoälyllä luodut FAQ-kierrokset ovat jättäneet kirjastoon päällekkäisyyksiä. Vain yksi duplikaattien poistotyö voi olla käynnissä tiliä kohden kerrallaan – toisen työn käynnistäminen samalla kun edellinen on vielä käynnissä palauttaa `409`.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `sourceIds` | Ei | Taulukko tietokannan lähdetunnisteista, joihin duplikaattien poisto rajataan. Jätä pois, jos haluat skannata koko FAQ-kirjastosi. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/dedupe?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/dedupe", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({}),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/dedupe",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={},
)
data = res.json()
```

**Vastaus** — `202 Accepted`

```json
{
  "success": true,
  "job_id": "dedupJob_aBc123"
}
```

Työ suoritetaan taustalla ja se kestää yleensä muutaman minuutin suuressa kirjastossa. Erillistä tilan päätepistettä ei ole – hae [`GET /faqs`](#list-faqs) uudelleen lyhyen odotuksen jälkeen nähdäksesi, mikä muuttui. Kun olet tarkistanut tuloksen, kutsu alla olevaa hylkäämisen päätepistettä sen tyhjentämiseksi.

### Hylkää duplikaattien tarkistustulos

`POST /faqs/dedupe/dismiss`

Tyhjentää valmistuneen duplikaattien poistotyön, jotta se ei enää näy aktiivisena tuloksena. Idempotentti – turvallinen kutsua, vaikka hylättävää ei olisikaan. Palauttaa `409`, jos työ on vielä `queued` tai `processing` (et voi hylätä ajoa, joka ei ole vielä valmistunut).

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/dedupe/dismiss?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/dedupe/dismiss", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/dedupe/dismiss",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

**Vastaus**

```json
{ "success": true }
```

### Luo UKK-kysymyksiä ladatuista tiedostoista

`POST /faqs/generate-from-documents`

Lukee yhden tai useamman tilisi tiedostotallennustilassa jo olevan asiakirjan ja antaa tekoälyn luonnostella UKK-kysymyksiä niiden sisällöstä. Luonnokset tarkistetaan olemassa olevaa kirjastoasi vasten, jotta merkinnät joko päivitetään tai niitä käytetään uudelleen sen sijaan, että luotaisiin kaksoiskappaleita. Tuloksia **ei** kirjoiteta välittömästi, vaan ne tallennetaan kampanjalle odottavaksi muutosjoukoksi, jonka voit tarkistaa ja sen jälkeen hyväksyä (tai hylätä) alla olevalla [Tarkistettujen UKK-muutosten soveltaminen](#apply-reviewed-faq-changes) -toiminnolla. Tämä kuluttaa krediittejä, koska kyseessä on tekoälypohjainen läpikäynti asiakirjan tekstistä.

Tämä päätepiste ei sisällä tiedostoa: `storagePath` on osoitettava tiedostoon, joka on jo omassa latauskansiossasi (`users/{your user id}/uploads/`), noudattaen samaa käytäntöä kuin [Ladattavan asiakirjan tuominen](knowledge-base.md#import-an-uploaded-document) tietokannan API:ssa.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `campaignId` | Kyllä | Kampanja, jolle luodut UKK-kysymykset on ehdotettu. |
| `uploadedFiles` | Kyllä | Tyhjästä poikkeava taulukko luettavista tiedostoista, jokainen `{ storagePath, fileName, mimeType }`. `storagePath` on alettava merkkijonolla `users/{your user id}/uploads/`. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/generate-from-documents?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "campaign123",
    "uploadedFiles": [
      { "storagePath": "users/abc123uid/uploads/handbook.pdf", "fileName": "handbook.pdf", "mimeType": "application/pdf" }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/generate-from-documents", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaignId: "campaign123",
    uploadedFiles: [
      { storagePath: "users/abc123uid/uploads/handbook.pdf", fileName: "handbook.pdf", mimeType: "application/pdf" },
    ],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/generate-from-documents",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaignId": "campaign123",
        "uploadedFiles": [
            {"storagePath": "users/abc123uid/uploads/handbook.pdf", "fileName": "handbook.pdf", "mimeType": "application/pdf"},
        ],
    },
)
data = res.json()
```

**Vastaus** — `202 Accepted`

```json
{
  "success": true,
  "faqCount": 6,
  "reusedCount": 2,
  "modifiedCount": 1,
  "newCount": 3
}
```

`faqCount` on tarkistusta odottavien ehdotettujen muutosten kokonaismäärä; `reusedCount`, `modifiedCount` ja `newCount` jakavat tämän määrän UKK-kysymyksiin, jotka vastasivat olemassa olevaa merkintää muuttumattomina, niihin, joita tekoäly ehdottaa muokattavaksi, sekä täysin uusiin. Ladatut tiedostot poistetaan tallennustilasta käsittelyn päätyttyä, riippumatta siitä, onnistuiko se vai ei.

### Tarkistettujen UKK-muutosten soveltaminen

`POST /faqs/apply-optimization`

Soveltaa (tai hylkää) tekoälyn ehdottaman UKK-muutosjoukon – sellaisen, joka on tuotettu yllä olevalla [Luo UKK-kysymyksiä asiakirjoista](#generate-faqs-from-uploaded-documents) -toiminnolla tai hallintapaneelin UKK-optimoinnin tarkistuksella. Valitset tarkalleen, mitkä ehdotetut muutokset hyväksyt; kaikkea, mitä et mainitse, ei muuteta (pois jätettyä muutosta ei koskaan käsitellä hylkäämisenä, joka poistaisi jotakin).

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `campaignId` | Toinen näistä kahdesta | Kampanja, jonka odottavia UKK-muutoksia sovelletaan. |
| `agentId` | Toinen näistä kahdesta | Tekoälyagentti, jonka odottavia UKK-muutoksia sovelletaan agenttipohjaisella tilillä. Anna täsmälleen yksi arvoista `campaignId` / `agentId`, ei koskaan molempia. |
| `acceptedChanges` | Kyllä | Taulukko hyväksymistäsi muutoksista, jokainen `{ action, faq_id?, faq_ref_path?, question?, answer?, edit_scope? }`. `action` on jokin arvoista `keep`, `remove`, `add_from_library`, `create_new`, `modify`. Lähetä tyhjä taulukko hylätäksesi odottavan joukon soveltamatta mitään. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/apply-optimization?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaignId": "campaign123",
    "acceptedChanges": [
      { "action": "create_new", "question": "Do you ship to the EU?", "answer": "Yes, EU shipping takes 7-10 business days." },
      { "action": "remove", "faq_ref_path": "users/abc123uid/faqs/oldFaqId" }
    ]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/apply-optimization", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaignId: "campaign123",
    acceptedChanges: [
      { action: "create_new", question: "Do you ship to the EU?", answer: "Yes, EU shipping takes 7-10 business days." },
      { action: "remove", faq_ref_path: "users/abc123uid/faqs/oldFaqId" },
    ],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/apply-optimization",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaignId": "campaign123",
        "acceptedChanges": [
            {"action": "create_new", "question": "Do you ship to the EU?", "answer": "Yes, EU shipping takes 7-10 business days."},
            {"action": "remove", "faq_ref_path": "users/abc123uid/faqs/oldFaqId"},
        ],
    },
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "message": "Applied 2 FAQ changes",
  "faq_count": 7
}
```

`faq_count` on kampanjan (tai agentin) linkitettyjen UKK-kysymysten kokonaismäärä soveltamisen jälkeen. Jos sovellettavaa odottavaa muutosjoukkoa ei ollut, vastauksena on `{ "success": true, "message": "No pending FAQ changes to apply" }`.

### Etsi tehtävän kaltaisia UKK-kysymyksiä

`POST /faqs/similar-for-task`

Asettaa UKK-kirjastosi tärkeysjärjestykseen tietovaje-tehtävän kysymyksen perusteella – sama haku, joka on hallintapaneelin "Käytä olemassa olevaa UKK-kysymystä" -valitsimen taustalla. Vain luku -muotoinen. `taskId` on osoitettava tyypin `faq_update` tehtävään.

Tämä päätepiste vastaa aina `200`, jopa odotetun virheen, kuten tuntemattoman tehtävän, kohdalla — tarkista `success` rungosta HTTP-tilan sijaan.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `taskId` | Kyllä | `faq_update`-tehtävä, jolle etsitään vastineita. |
| `limit` | Ei | Palautettavien vastineiden enimmäismäärä. Oletusarvo on 20, enimmäismäärä 50. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/similar-for-task?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "taskId": "task789", "limit": 10 }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/similar-for-task", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ taskId: "task789", limit: 10 }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/similar-for-task",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"taskId": "task789", "limit": 10},
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "data": {
    "task_id": "task789",
    "matches": [
      {
        "faq_id": "aBcD1234eFgH5678",
        "question": "How long does shipping take?",
        "answer": "Standard shipping takes 3-5 business days.",
        "category": "shipping",
        "created_at": "2026-01-01T12:00:00.000Z",
        "similarity": 0.81,
        "embedding_similarity": 0.81,
        "keyword_similarity": 0.6,
        "bm25_score": 4.2,
        "distance": 0.19
      }
    ]
  }
}
```

Vastineet on lajiteltu `similarity` mukaan (semanttinen vastaavuus, jos saatavilla, muuten avainsanojen päällekkäisyys), parhaat ensin. Pehmeässä virheessä muoto on `{ "success": false, "error": "...", "error_code": 404 }` — `error_code` heijastaa sitä, mikä HTTP-tila normaalisti olisi.

### Tehtävän ratkaiseminen olemassa olevalla UKK:lla

`POST /faqs/resolve-task`

Ratkaisee tietovaje-tehtävän linkittämällä sen jo olemassa olevaan UKK:hon (sen sijaan, että kirjoittaisit uuden), lähettää kyseisen UKK:n vastauksen yhteydenottohenkilölle, joka laukaisi vajeen, ja merkitsee tehtävän valmiiksi. Käytä tätä sen jälkeen, kun [Etsi tehtävää vastaavia UKK:ita](#find-faqs-similar-to-a-task) löytää olemassa olevan UKK:n, joka kattaa kysymyksen.

Kuten yllä oleva päätepiste, tämä vastaa aina `200` — tarkista `success` rungosta.

**Pyynnön kentät**

| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
| `taskId` | Kyllä | `faq_update`-tehtävä, joka ratkaistaan. |
| `faqId` | Kyllä | Olemassa oleva UKK, joka linkitetään ja lähetetään vastauksena. |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/faqs/resolve-task?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "taskId": "task789", "faqId": "aBcD1234eFgH5678" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/faqs/resolve-task", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ taskId: "task789", faqId: "aBcD1234eFgH5678" }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/faqs/resolve-task",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"taskId": "task789", "faqId": "aBcD1234eFgH5678"},
)
data = res.json()
```

**Vastaus**

```json
{
  "success": true,
  "data": {
    "task_id": "task789",
    "faq_id": "aBcD1234eFgH5678",
    "follow_up_status": "published"
  }
}
```

`follow_up_status` kertoo, mitä yhteydenoton seurannalle tapahtui: `published` (lähetetty heti), `queued` (tekoäly oli jo vastaamassa kyseiselle henkilölle, joten se lähetetään seuraavaksi), `skipped_no_contact` (tehtävään ei ole linkitetty yhteyshenkilöä) tai `skipped_no_campaign` (ei kampanjaa, jonka kautta lähettää).

---

## UKK-rajapinnan virheet

UKK-päätepisteet palauttavat vakioidun virhekuoren:

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

| Tila | Milloin se tapahtuu UKK-päätepisteessä |
|---|---|
| `400` | Pakollinen kenttä puuttuu tai on virheellinen (esimerkiksi tyhjä `question`, puuttuva `campaign_id` tai yli 500 kohdetta massapyynnössä). |
| `404` | UKK:ta tai kampanjaa ei löytynyt — joko sitä ei ole olemassa tai se kuuluu toiselle tilille. |
| `409` | `POST /faqs/dedupe` kutsuttiin, kun duplikaattien poistotyö on jo `queued`/`processing`, tai `POST /faqs/dedupe/dismiss` kutsuttiin, kun työ ei ole vielä valmistunut. |

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).

`POST /faqs/similar-for-task` ja `POST /faqs/resolve-task` ovat tämän sivun kaksi poikkeusta: ne vastaavat `200` jopa odotetun virheen kohdalla (tuntematon tehtävä, väärä tehtävätyyppi) ja asettavat todellisen tilan rungon `error_code`-kohtaan — katso kunkin päätepisteen kohdalta yllä.

---

## Aiheeseen liittyvää

- [Kampanjoiden API](campaigns.md) — kampanjat, joihin UKK:t on linkitetty.
- [Tietokannan API](knowledge-base.md) — tuo verkkosivustoja ja asiakirjoja UKK:iksi automaattisesti ja niputa UKK:t uudelleenkäytettäviin tietoryhmiin.
- [API-käyttöoikeus](../integrations/api-access.md) — luo API-avaimesi.
- [Todennus](authentication.md) — kaikki tavat välittää avaimesi.
