
# API FAQ

FAQ to wpisy w formie pytań i odpowiedzi, z których korzysta Twój bot AI podczas odpowiadania klientom. Każde FAQ jest przypisane do Twojego konta i może być powiązane z jedną lub wieloma kampaniami, dzięki czemu tę samą odpowiedź można wykorzystać wszędzie tam, gdzie jest to istotne. API FAQ pozwala na programowe zarządzanie tą biblioteką — tworzenie, aktualizowanie, importowanie masowe, zmianę kolejności oraz łączenie FAQ z kampaniami bezpośrednio z poziomu własnego kodu.

Wszystkie poniższe punkty końcowe odnoszą się do bazowego adresu URL `https://api.youraiconnector.com/v1`. Każde żądanie musi zostać uwierzytelnione — zobacz [Dostęp do API](../integrations/api-access.md) oraz [Uwierzytelnianie](authentication.md). Dostęp do API jest funkcją płatną; bez niego żądania będą odrzucane z błędem `403`.

> **Jak bot korzysta z FAQ:** Gdy tworzysz lub zmieniasz FAQ, platforma w tle przygotowuje dane wyszukiwania (używane do dopasowywania FAQ do przychodzących pytań). Zazwyczaj trwa to kilka sekund, po czym bot automatycznie zaczyna korzystać z danego wpisu.


---

## Obiekt FAQ

Każde FAQ zwracane przez API ma następującą strukturę:

| Pole | Typ | Opis |
|---|---|---|
| `id` | string | Unikalny identyfikator FAQ. |
| `question` | string | Pytanie klienta, na które odpowiada ten wpis. |
| `answer` | string | Odpowiedź udzielana przez bota AI. |
| `category` | string \| null | Opcjonalna etykieta kategorii w dowolnym formacie. |
| `tags` | string[] | Opcjonalne etykiety do organizowania wpisów FAQ. |
| `is_active` | boolean | Czy bot może korzystać z tego wpisu FAQ. Domyślnie `true`. |
| `is_global` | boolean | Oznacza, że FAQ nie jest powiązane z jedną konkretną kampanią lub Agentem. Nie oznacza to, że FAQ ma zastosowanie wszędzie: FAQ jest używane tylko przez kampanie i Agentów, z którymi jest powiązane. Domyślnie `false`. |
| `usage_count` | integer | Liczba użyć tego wpisu FAQ w odpowiedziach AI. |
| `order_index` | integer | Pozycja wyświetlania tego wpisu FAQ w ramach kampanii. |
| `campaign_ids` | string[] | Identyfikatory kampanii, z którymi powiązane jest to FAQ. |
| `created_at` | string \| null | Znacznik czasu ISO 8601 utworzenia FAQ. |
| `updated_at` | string \| null | Znacznik czasu ISO 8601 ostatniej zmiany. |

Pola, które możesz **ustawić**, to: `question`, `answer`, `is_active`, `is_global`, `category`, `tags` oraz `order_index`. Platforma zarządza wszystkim innym (dane wyszukiwania, liczby użyć, znaczniki czasu); wszelkie inne pola w treści żądania są ignorowane.

---

## Lista FAQ

`GET /faqs`

Zwraca listę FAQ na Twoim koncie, zaczynając od najnowszych. Opcjonalnie można filtrować wyniki według konkretnej kampanii lub stanu aktywności.

**Parametry zapytania**

| Parametr | Wymagany | Opis |
|---|---|---|
| `campaign_id` | Nie | Zwróć tylko FAQ powiązane z tą kampanią. |
| `is_active` | Nie | Zwróć tylko FAQ o tym stanie aktywności (`true` lub `false`). Ten filtr jest stosowany dla każdej strony, więc strona może zawierać mniej elementów niż `limit`. |
| `limit` | Nie | Maksymalna liczba FAQ na stronę. Domyślnie `50`, maksymalnie `100`. |
| `cursor` | Nie | Identyfikator FAQ, od którego należy kontynuować. Przekaż wartość `next_cursor` z poprzedniej strony. |

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

**Odpowiedź**

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

Gdy `next_cursor` wynosi `null`, nie ma więcej wyników.

---

## Pobierz FAQ

`GET /faqs/{faqId}`

Zwraca pojedyncze FAQ na podstawie jego identyfikatora.

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

**Odpowiedź**

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

---

## Utwórz FAQ

`POST /faqs`

Tworzy nowe FAQ i łączy je z kampanią.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `campaign_id` | Tak | Kampania, z którą ma zostać powiązane nowe FAQ. |
| `question` | Tak | Pytanie klienta, na które odpowiada ten wpis. |
| `answer` | Tak | Odpowiedź, której powinien udzielić bot. |
| `is_active` | Nie | Czy bot może używać tego FAQ. Wartość domyślna to `true`. |
| `is_global` | Nie | Czy FAQ dotyczy wszystkich kampanii. Wartość domyślna to `false`. |
| `category` | Nie | Dowolna etykieta kategorii. |
| `tags` | Nie | Tablica etykiet. |
| `order_index` | Nie | Pozycja wyświetlania w ramach kampanii. Wartość domyślna to `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"]
```

**Odpowiedź**

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

---

## Zaktualizuj FAQ

`PUT /faqs/{faqId}`

Częściowo aktualizuje FAQ. Zmieniane są tylko podane pola z możliwością zapisu; wszystkie inne zachowują swoją bieżącą wartość. Zmiana `question` lub `answer` automatycznie odświeża dane wyszukiwania FAQ w tle.

Jeśli wyślesz `question` lub `answer`, muszą to być niepuste ciągi znaków. Przesłanie braku rozpoznanych pól z możliwością zapisu spowoduje zwrócenie `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()
```

**Odpowiedź**

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

---

## Usuń FAQ

`DELETE /faqs/{faqId}`

Trwale usuwa FAQ. Opcjonalnie przekaż `campaign_id` jako parametr zapytania, aby również usunąć FAQ z listy FAQ danej kampanii.

**Parametry zapytania**

| Parametr | Wymagany | Opis |
|---|---|---|
| `campaign_id` | Nie | Usuń również FAQ z listy FAQ tej kampanii. |

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

**Odpowiedź**

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

---

## Masowe usuwanie FAQ

`POST /faqs/bulk-delete`

Usuwa do 500 FAQ w jednym żądaniu. Gdy podano `campaign_id`, usunięte FAQ są również usuwane z listy FAQ danej kampanii.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `faq_ids` | Tak | Niepusta tablica identyfikatorów FAQ do usunięcia (maks. 500). |
| `campaign_id` | Nie | Usuń również usunięte FAQ z listy FAQ tej kampanii. |

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

**Odpowiedź**

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

---

## Importuj FAQ

`POST /faqs/import`

Masowy import do 500 często zadawanych pytań (FAQ) i powiązanie ich wszystkich z jedną kampanią. Elementy, których `question` pasuje do istniejącego FAQ w Twojej bibliotece (wielkość liter nie ma znaczenia), **aktualizują** to FAQ zamiast tworzyć duplikat.

> **Wskazówka dotycząca wydajności:** Dopasowywanie duplikatów przeszukuje całą bibliotekę FAQ, więc bardzo duże biblioteki spowalniają import. Zaleca się wykonywanie mniejszej liczby większych importów zamiast wielu małych.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `campaign_id` | Tak | Kampania, z którą powiązane są wszystkie importowane FAQ. |
| `faqs` | Tak | Niepusta tablica elementów FAQ (maks. 500). Każdy element musi mieć niepuste `question` i `answer`; może również zawierać `is_active`, `is_global`, `category`, `tags` oraz `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()
```

**Odpowiedź**

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

`faq_ids` to identyfikatory utworzonych lub zaktualizowanych FAQ, w kolejności, w jakiej zostały dostarczone.

---

## Zmiana kolejności FAQ

`POST /faqs/reorder`

Ustawia kolejność wyświetlania FAQ w kampanii. Podaj **pełną** listę identyfikatorów FAQ w żądanej kolejności; pozycja każdego FAQ zostanie zaktualizowana tak, aby odpowiadała jego miejscu w tablicy.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `campaign_id` | Tak | Kampania, której FAQ mają zostać uporządkowane. |
| `ordered_faq_ids` | Tak | Niepusta tablica wszystkich identyfikatorów FAQ kampanii w żądanej kolejności wyświetlania (maks. 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()
```

**Odpowiedź**

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

Jeśli kampania lub którykolwiek z identyfikatorów FAQ nie zostanie znaleziony na Twoim koncie, żądanie zwróci `404 One or more FAQs were not found`.

---

## Powiąż FAQ z kampanią

`POST /faqs/{faqId}/link`

Łączy istniejące FAQ z dodatkową kampanią. FAQ może być współdzielone przez dowolną liczbę kampanii, dzięki czemu tę samą odpowiedź wystarczy utrzymywać tylko raz.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `campaign_id` | Tak | Kampania, z którą ma zostać powiązane FAQ. |

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

**Odpowiedź**

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

---

## Odłącz FAQ od kampanii

`POST /faqs/{faqId}/unlink`

Usuwa FAQ z kampanii bez usuwania samego FAQ. FAQ pozostaje w bibliotece i zachowuje powiązania z innymi kampaniami.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `campaign_id` | Tak | Kampania, z której ma zostać usunięte FAQ. |

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

**Odpowiedź**

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

---

## Przebuduj dane wyszukiwania FAQ

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

Kolejkuje przebudowę danych używanych przez bota AI do znajdowania tego FAQ (dane wyszukiwania semantycznego i słów kluczowych). Jest to przydatne, jeśli FAQ nie jest uwzględniane w odpowiedziach zgodnie z oczekiwaniami. Przebudowa odbywa się w tle i zazwyczaj kończy się w ciągu kilku sekund; FAQ może być tymczasowo wykluczone z odpowiedzi AI podczas trwania procesu.

Ten punkt końcowy zwraca `202 Accepted`, ponieważ praca jest kontynuowana po wysłaniu odpowiedzi. Pole `status` ma zawsze wartość `"processing"` — pobierz ponownie FAQ później, jeśli chcesz potwierdzić zakończenie procesu.

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

**Odpowiedź**

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

---

## Zarządzanie FAQ wspomagane przez AI

Poniższe punkty końcowe wykraczają poza zwykłe operacje CRUD: wywołują te same narzędzia wspomagane przez AI, z których korzysta edytor FAQ w panelu nawigacyjnym — wyszukują duplikaty, generują wpisy z dokumentu i dopasowują FAQ do otwartych zadań związanych z lukami w wiedzy. Treści żądań w tym zestawie używają nazw pól `camelCase` (`campaignId`, `taskId`, `sourceIds`...), pasujących do własnych struktur żądań aplikacji, a nie `snake_case` używanych w innych miejscach na tej stronie — kopiuj poniższe przykłady zamiast zgadywać nazwę pola.

### Utwórz kopię FAQ przeznaczoną tylko dla kampanii

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

Tworzy nowe FAQ, które jest kopią istniejącego, ograniczone do pojedynczej kampanii, i ponownie łączy tę kampanię z nową kopią zamiast z oryginałem. Użyj tego, gdy chcesz dostosować odpowiedź dla jednej kampanii bez zmieniania jej wszędzie tam, gdzie używane jest oryginalne FAQ. Oryginalne FAQ pozostaje na miejscu — traci jedynie powiązanie z tą kampanią.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `campaign_id` | Tak | Kampania, do której ma zostać ograniczona nowa kopia i z której ma zostać przeniesione powiązanie z oryginalnego FAQ. |
| `question` | Tak | Pytanie dla nowej, specyficznej dla kampanii kopii. |
| `answer` | Tak | Odpowiedź dla nowej, specyficznej dla kampanii kopii. |

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

**Odpowiedź** — `201 Created`

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

### Znajdź niemal identyczne FAQ

`POST /faqs/dedupe`

Uruchamia zadanie w tle, które skanuje bibliotekę FAQ w poszukiwaniu niemal identycznych i nakładających się wpisów, a następnie scala je lub usuwa, jeśli jest to pewne. Przydatne po masowym imporcie lub po kilku rundach generowania FAQ przez AI, które pozostawiły w bibliotece nakładające się treści. Na jedno konto może być uruchomione tylko jedno zadanie deduplikacji naraz — uruchomienie drugiego, gdy zadanie jest w toku, zwróci `409`.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `sourceIds` | Nie | Tablica identyfikatorów źródeł bazy wiedzy, do których ma zostać ograniczona deduplikacja. Pomiń, aby przeskanować całą bibliotekę FAQ. |

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

**Odpowiedź** — `202 Accepted`

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

Zadanie działa w tle i zazwyczaj zajmuje kilka minut w przypadku dużej biblioteki. Nie ma oddzielnego punktu końcowego statusu — pobierz ponownie [`GET /faqs`](#list-faqs) po krótkim oczekiwaniu, aby zobaczyć, co się zmieniło. Po zakończeniu przeglądania wyników wywołaj poniższy punkt końcowy odrzucenia, aby je wyczyścić.

### Odrzuć wynik sprawdzania duplikatów

`POST /faqs/dedupe/dismiss`

Czyści zakończone zadanie deduplikacji, dzięki czemu przestaje ono być wyświetlane jako aktywny wynik. Idempotentne — bezpieczne do wywołania, nawet jeśli nie ma nic do odrzucenia. Zwraca `409`, jeśli zadanie jest nadal `queued` lub `processing` (nie można odrzucić uruchomienia, które się nie zakończyło).

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

**Odpowiedź**

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

### Generowanie FAQ z przesłanych dokumentów

`POST /faqs/generate-from-documents`

Odczytuje jeden lub więcej dokumentów znajdujących się już w pamięci plików Twojego konta i zleca sztucznej inteligencji przygotowanie szkiców FAQ na podstawie ich treści, sprawdzając je pod kątem istniejącej biblioteki, aby ponownie wykorzystać lub zaktualizować wpisy zamiast tworzyć duplikaty. Wyniki **nie** są zapisywane natychmiast — są przechowywane jako oczekujący zestaw zmian w kampanii do Twojego przeglądu, a następnie stosowane (lub odrzucane) za pomocą [Zastosuj przejrzane zmiany FAQ](#apply-reviewed-faq-changes) poniżej. Operacja ta kosztuje kredyty, ponieważ jest to proces generowania przez sztuczną inteligencję na podstawie tekstu dokumentu.

Ten punkt końcowy nie przesyła pliku: `storagePath` musi wskazywać na plik znajdujący się już w Twoim folderze przesyłania (`users/{your user id}/uploads/`), zgodnie z tą samą konwencją co [Importuj przesłany dokument](knowledge-base.md#import-an-uploaded-document) w API bazy wiedzy.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `campaignId` | Tak | Kampania, dla której proponowane są wygenerowane FAQ. |
| `uploadedFiles` | Tak | Niepusta tablica plików do odczytania, każdy `{ storagePath, fileName, mimeType }`. `storagePath` musi zaczynać się od `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()
```

**Odpowiedź** — `202 Accepted`

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

`faqCount` to całkowita liczba proponowanych zmian oczekujących na przegląd; `reusedCount`, `modifiedCount` i `newCount` dzielą tę liczbę na FAQ, które pasowały do istniejącego wpisu bez zmian, te, które sztuczna inteligencja proponuje edytować, oraz zupełnie nowe. Przesłane pliki są usuwane z pamięci po zakończeniu przetwarzania, niezależnie od tego, czy zakończyło się ono sukcesem.

### Zastosuj przejrzane zmiany FAQ

`POST /faqs/apply-optimization`

Stosuje (lub odrzuca) oczekujący zestaw zmian FAQ zaproponowanych przez sztuczną inteligencję — tego typu, jaki jest tworzony przez [Generowanie FAQ z dokumentów](#generate-faqs-from-uploaded-documents) powyżej lub przez przegląd optymalizacji FAQ w panelu nawigacyjnym. Wybierasz dokładnie, które z proponowanych zmian zaakceptować; wszystko, czego nie wymienisz, pozostaje nienaruszone (pominięta zmiana nigdy nie jest traktowana jako odrzucenie, które usuwa cokolwiek).

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `campaignId` | Jedno z tych dwóch | Kampania, której oczekujące zmiany FAQ są stosowane. |
| `agentId` | Jedno z tych dwóch | Agent AI, którego oczekujące zmiany FAQ są stosowane na koncie natywnym dla agenta. Podaj dokładnie jedno z `campaignId` / `agentId`, nigdy oba. |
| `acceptedChanges` | Tak | Tablica zaakceptowanych zmian, każda `{ action, faq_id?, faq_ref_path?, question?, answer?, edit_scope? }`. `action` to jedno z `keep`, `remove`, `add_from_library`, `create_new`, `modify`. Wyślij pustą tablicę, aby odrzucić oczekujący zestaw bez stosowania jakichkolwiek zmian. |

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

**Odpowiedź**

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

`faq_count` to całkowita liczba powiązanych FAQ kampanii (lub Agenta) po zastosowaniu zmian. Jeśli nie było oczekującego zestawu zmian do zastosowania, odpowiedzią jest `{ "success": true, "message": "No pending FAQ changes to apply" }`.

### Znajdź FAQ podobne do zadania

`POST /faqs/similar-for-task`

Szereguje Twoją bibliotekę FAQ według trafności względem pytania w zadaniu dotyczącym luki w wiedzy — to samo wyszukiwanie, które znajduje się za selektorem "Użyj istniejącego FAQ" w panelu nawigacyjnym. Tylko do odczytu. `taskId` musi wskazywać na zadanie typu `faq_update`.

Ten punkt końcowy zawsze odpowiada `200`, nawet w przypadku oczekiwanego błędu, takiego jak nieznane zadanie — sprawdź `success` w treści zamiast statusu HTTP.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `taskId` | Tak | Zadanie `faq_update`, dla którego mają zostać znalezione dopasowania. |
| `limit` | Nie | Maksymalna liczba zwracanych dopasowań. Wartość domyślna to 20, limit wynosi 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()
```

**Odpowiedź**

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

Dopasowania są sortowane według `similarity` (dopasowanie semantyczne, jeśli jest dostępne, w przeciwnym razie nakładanie się słów kluczowych), od najlepszego. W przypadku błędu miękkiego format odpowiedzi to `{ "success": false, "error": "...", "error_code": 404 }` — `error_code` odzwierciedla to, jaki byłby normalnie status HTTP.

### Rozwiąż zadanie za pomocą istniejącego FAQ

`POST /faqs/resolve-task`

Rozwiązuje zadanie dotyczące luki w wiedzy poprzez powiązanie go z istniejącym FAQ (zamiast tworzenia nowego), wysyła odpowiedź z tego FAQ do kontaktu, który zgłosił lukę, i oznacza zadanie jako ukończone. Użyj tego po tym, jak [Znajdź FAQ podobne do zadania](#find-faqs-similar-to-a-task) wskaże istniejące FAQ, które już zawiera odpowiedź na to pytanie.

Podobnie jak w przypadku powyższego punktu końcowego, ten zawsze odpowiada `200` — sprawdź `success` w treści.

**Pola żądania**

| Pole | Wymagane | Opis |
|---|---|---|
| `taskId` | Tak | Zadanie `faq_update` do rozwiązania. |
| `faqId` | Tak | Istniejące FAQ, które należy powiązać i wysłać jako odpowiedź. |

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

**Odpowiedź**

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

`follow_up_status` informuje o tym, co stało się z działaniem następczym wobec kontaktu: `published` (wysłano natychmiast), `queued` (sztuczna inteligencja była już w trakcie odpowiadania temu kontaktowi, więc wiadomość zostanie wysłana później), `skipped_no_contact` (zadanie nie ma powiązanego kontaktu) lub `skipped_no_campaign` (brak kampanii, przez którą można by to wysłać).

---

## Błędy API FAQ

Punkty końcowe FAQ zwracają standardową kopertę błędu:

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

| Status | Kiedy występuje w punkcie końcowym FAQ |
|---|---|
| `400` | Brakuje wymaganego pola lub jest ono nieprawidłowe (na przykład puste `question`, brakujące `campaign_id` lub ponad 500 elementów w żądaniu zbiorczym). |
| `404` | Nie znaleziono FAQ lub kampanii — albo nie istnieją, albo należą do innego konta. |
| `409` | Wywołano `POST /faqs/dedupe`, podczas gdy zadanie deduplikacji jest już `queued`/`processing`, lub wywołano `POST /faqs/dedupe/dismiss`, podczas gdy zadanie jeszcze się nie zakończyło. |

Wspólne kody, które może zwrócić każdy punkt końcowy — `401`, `403` (Twój plan nie obejmuje dostępu do API), `429` (limit szybkości) oraz `500` — zostały wymienione wraz ze wskazówkami dotyczącymi ponawiania prób w sekcji [Błędy i stronicowanie](errors-and-pagination.md).

`POST /faqs/similar-for-task` i `POST /faqs/resolve-task` to dwa wyjątki na tej stronie: odpowiadają `200` nawet w przypadku oczekiwanego błędu (nieznane zadanie, niewłaściwy typ zadania) i umieszczają rzeczywisty status w `error_code` w treści — zobacz każdy z powyższych punktów końcowych.

---

## Powiązane

- [API kampanii](campaigns.md) — kampanie, z którymi powiązane są Twoje FAQ.
- [API bazy wiedzy](knowledge-base.md) — automatyczne importowanie stron internetowych i dokumentów do FAQ oraz łączenie FAQ w grupy wiedzy wielokrotnego użytku.
- [Dostęp do API](../integrations/api-access.md) — generowanie klucza API.
- [Uwierzytelnianie](authentication.md) — wszystkie sposoby przekazywania klucza.
