
# FAQs API

FAQ'er er de spørgsmål-og-svar-poster, som din AI-bot trækker på, når den svarer kunder. Hver FAQ tilhører din konto og kan linkes til en eller flere kampagner, så det samme svar kan genbruges overalt, hvor det er relevant. FAQs API'et lader dig administrere dette bibliotek programmatisk — opret, opdater, importér i bulk, omarrangér og link FAQ'er til kampagner fra din egen kode.

Alle endpoints herunder er relative til basis-URL'en `https://api.youraiconnector.com/v1`. Hver anmodning skal godkendes — se [API-adgang](../integrations/api-access.md) og [Godkendelse](authentication.md). API-adgang er en betalt funktion; uden den afvises anmodninger med en `403`.

> **Hvordan botten bruger en FAQ:** Når du opretter eller ændrer en FAQ, forbereder platformen dens søgedata (der bruges til at matche FAQ'en med indgående spørgsmål) i baggrunden. Dette tager normalt få sekunder, hvorefter botten automatisk begynder at bruge posten.


---

## FAQ-objektet

Hver FAQ, der returneres fra API'et, har denne form:

| Felt | Type | Beskrivelse |
|---|---|---|
| `id` | string | FAQ'ens unikke identifikator. |
| `question` | string | Kundespørgsmålet, som dette punkt besvarer. |
| `answer` | string | Svaret, som AI-botten giver. |
| `category` | string \| null | Valgfri kategorimærkat i frit format. |
| `tags` | string[] | Valgfrie mærkater til organisering af FAQ'er. |
| `is_active` | boolean | Om botten har tilladelse til at bruge denne FAQ. Standard er `true`. |
| `is_global` | boolean | Marker FAQ'en som ikke bundet til en specifik kampagne eller agent. Det gør ikke, at FAQ'en gælder overalt: en FAQ bruges kun af de kampagner og agenter, den er linket til. Standard er `false`. |
| `usage_count` | integer | Hvor mange gange denne FAQ er blevet brugt i AI-svar. |
| `order_index` | integer | Visningsposition for denne FAQ inden for dens kampagne. |
| `campaign_ids` | string[] | ID'er på de kampagner, som denne FAQ er linket til. |
| `created_at` | string \| null | ISO 8601-tidsstempel for hvornår FAQ'en blev oprettet. |
| `updated_at` | string \| null | ISO 8601-tidsstempel for den seneste ændring. |

De felter, du kan **indstille**, er: `question`, `answer`, `is_active`, `is_global`, `category`, `tags` og `order_index`. Platformen administrerer alt andet (søgedata, brugstællinger, tidsstempler); alle andre felter i din anmodningskrop ignoreres.

---

## List FAQ'er

`GET /faqs`

Returnerer FAQ'erne på din konto, nyeste først. Filtrér eventuelt til en enkelt kampagne eller efter aktiv status.

**Forespørgselsparametre**

| Parameter | Påkrævet | Beskrivelse |
|---|---|---|
| `campaign_id` | Nej | Returnér kun FAQ'er linket til denne kampagne. |
| `is_active` | Nej | Returnér kun FAQ'er med denne aktive status (`true` eller `false`). Dette filter anvendes pr. side, så en side kan indeholde færre elementer end `limit`. |
| `limit` | Nej | Maksimalt antal FAQ'er pr. side. Standard `50`, maksimum `100`. |
| `cursor` | Nej | Et FAQ-ID at fortsætte efter. Indsæt `next_cursor`-værdien fra den forrige side. |

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

**Svar**

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

Når `next_cursor` er `null`, er der ikke flere resultater.

---

## Hent en FAQ

`GET /faqs/{faqId}`

Returnerer en enkelt FAQ via dens ID.

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

**Svar**

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

---

## Opret en FAQ

`POST /faqs`

Opretter en ny FAQ og linker den til en kampagne.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `campaign_id` | Ja | Kampagnen, som den nye FAQ skal linkes til. |
| `question` | Ja | Kundespørgsmålet, som dette punkt besvarer. |
| `answer` | Ja | Svaret, som botten skal give. |
| `is_active` | Nej | Hvorvidt botten må bruge denne FAQ. Standard er `true`. |
| `is_global` | Nej | Hvorvidt FAQ'en gælder for alle kampagner. Standard er `false`. |
| `category` | Nej | En frit definerbar kategorietiket. |
| `tags` | Nej | Et array af etiketter. |
| `order_index` | Nej | Visningsposition inden for kampagnen. Standard er `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"]
```

**Svar**

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

---

## Opdater en FAQ

`PUT /faqs/{faqId}`

Opdaterer delvist en FAQ. Kun de angivne skrivbare felter ændres; alt andet beholder sin nuværende værdi. Ændring af `question` eller `answer` opdaterer automatisk FAQ'ens søgedata i baggrunden.

Hvis du sender `question` eller `answer`, skal de være ikke-tomme strenge. Hvis der ikke sendes nogen genkendte skrivbare felter, returneres en `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()
```

**Svar**

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

---

## Slet en FAQ

`DELETE /faqs/{faqId}`

Sletter en FAQ permanent. Send eventuelt `campaign_id` som en forespørgselsparameter for også at fjerne FAQ'en fra den kampagnes FAQ-liste.

**Forespørgselsparametre**

| Parameter | Påkrævet | Beskrivelse |
|---|---|---|
| `campaign_id` | Nej | Fjern også FAQ'en fra denne kampagnes FAQ-liste. |

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

**Svar**

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

---

## Massesletning af FAQ'er

`POST /faqs/bulk-delete`

Sletter op til 500 FAQ'er i en enkelt anmodning. Når `campaign_id` angives, fjernes de slettede FAQ'er også fra den pågældende kampagnes FAQ-liste.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `faq_ids` | Ja | Et ikke-tomt array af FAQ-id'er, der skal slettes (maks. 500). |
| `campaign_id` | Nej | Fjern også de slettede FAQ'er fra denne kampagnes FAQ-liste. |

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

**Svar**

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

---

## Importér FAQ'er

`POST /faqs/import`

Masseimportér op til 500 FAQ'er og link dem alle til én kampagne. Elementer, hvis `question` matcher en eksisterende FAQ i dit bibliotek (uafhængigt af store/små bogstaver), **opdaterer** den pågældende FAQ i stedet for at oprette en dublet.

> **Ydelsestip:** Duplikatsøgning scanner hele dit FAQ-bibliotek, så meget store biblioteker gør importer langsommere. Foretræk færre, større importer frem for mange små.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `campaign_id` | Ja | Den kampagne, som alle importerede FAQ'er er knyttet til. |
| `faqs` | Ja | Et ikke-tomt array af FAQ-elementer (maks. 500). Hvert element skal have et ikke-tomt `question` og `answer`; det kan også indeholde `is_active`, `is_global`, `category`, `tags` og `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()
```

**Svar**

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

`faq_ids` er de oprettede eller opdaterede FAQ-id'er i den rækkefølge, du angav dem.

---

## Omorganiser FAQ'er

`POST /faqs/reorder`

Angiver visningsrækkefølgen for en kampagnes FAQ'er. Angiv den **fulde** liste over FAQ-id'er i den ønskede rækkefølge; hver FAQ's position opdateres, så den matcher dens plads i arrayet.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `campaign_id` | Ja | Den kampagne, hvis FAQ'er skal omorganiseres. |
| `ordered_faq_ids` | Ja | Et ikke-tomt array af alle kampagnens FAQ-id'er i den ønskede visningsrækkefølge (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()
```

**Svar**

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

Hvis kampagnen eller nogen af FAQ-id'erne ikke findes på din konto, returnerer anmodningen `404 One or more FAQs were not found`.

---

## Knyt en FAQ til en kampagne

`POST /faqs/{faqId}/link`

Knytter en eksisterende FAQ til en yderligere kampagne. En FAQ kan deles af et vilkårligt antal kampagner, så det samme svar behøver kun at blive vedligeholdt én gang.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `campaign_id` | Ja | Kampagnen, som FAQ'en skal linkes til. |

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

**Svar**

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

---

## Fjern link fra en FAQ til en kampagne

`POST /faqs/{faqId}/unlink`

Fjerner en FAQ fra en kampagne uden at slette selve FAQ'en. FAQ'en forbliver i dit bibliotek og forbliver linket til alle andre kampagner.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `campaign_id` | Ja | Kampagnen, som FAQ'en skal fjernes fra. |

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

**Svar**

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

---

## Gendan en FAQ's søgedata

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

Sætter en gendannelse af de data, som AI-botten bruger til at finde denne FAQ (dens semantiske og søgeordsdata), i kø. Dette er nyttigt, hvis en FAQ ikke bliver fundet i svar som forventet. Gendannelsen kører i baggrunden og fuldføres normalt inden for få sekunder; FAQ'en kan være midlertidigt udelukket fra AI-svar, mens den gendannes.

Dette endpoint returnerer `202 Accepted`, fordi arbejdet fortsætter, efter svaret er sendt. `status` er altid `"processing"` — hent FAQ'en igen senere, hvis du har brug for at bekræfte fuldførelsen.

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

**Svar**

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

---

## AI-assisteret FAQ-styring

Slutpunkterne herunder går ud over almindelig CRUD: De kalder de samme AI-assistentværktøjer, som dashboardets FAQ-editor bruger — de finder dubletter, genererer indlæg fra et dokument og matcher FAQ'er med åbne videnshulsopgaver. Anmodningsorganer i dette sæt bruger `camelCase` feltnavne (`campaignId`, `taskId`, `sourceIds`...), der matcher appens egne anmodningsformer, frem for de `snake_case`, der bruges andre steder på denne side — kopier eksemplerne herunder i stedet for at gætte et feltnavn.

### Forgren en FAQ til en kampagne-specifik kopi

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

Opretter en ny FAQ, der er en kopi af en eksisterende, begrænset til en enkelt kampagne, og genforbinder den kampagne til den nye kopi i stedet for den originale. Brug dette, når du vil tilpasse et svar til én kampagne uden at ændre det alle andre steder, hvor den originale FAQ bruges. Den originale FAQ forbliver intakt — den mister blot linket til denne kampagne.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `campaign_id` | Ja | Kampagnen, som den nye kopi skal begrænses til, og som skal genforbindes fra den originale FAQ. |
| `question` | Ja | Spørgsmålet til den nye, kampagnespecifikke kopi. |
| `answer` | Ja | Svaret til den nye, kampagnespecifikke kopi. |

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

**Svar** — `201 Created`

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

### Find næsten identiske FAQ'er

`POST /faqs/dedupe`

Starter et baggrundsjob, der scanner dit FAQ-bibliotek for næsten identiske og overlappende indlæg og fletter eller fjerner dem, hvor det er sikkert. Nyttigt efter en bulk-import, eller efter flere runder med AI-genererede FAQ'er har efterladt biblioteket med overlap. Kun ét deduplikeringsjob kan køre pr. konto ad gangen — hvis du starter et andet, mens et job stadig kører, returneres `409`.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `sourceIds` | Nej | Array af vidensbase-kilde-ID'er til at begrænse deduplikeringen til. Udelad for at scanne hele dit FAQ-bibliotek. |

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

**Svar** — `202 Accepted`

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

Jobbet kører i baggrunden og tager typisk et par minutter på et stort bibliotek. Der er ikke et separat statusslutpunkt — hent [`GET /faqs`](#list-faqs) igen efter en kort ventetid for at se, hvad der er ændret. Når du er færdig med at gennemgå resultatet, skal du kalde afvisnings-slutpunktet herunder for at rydde det.

### Afvis et resultat fra dublet-tjek

`POST /faqs/dedupe/dismiss`

Rydder det afsluttede deduplikeringsjob, så det ikke længere vises som et aktivt resultat. Idempotent — sikkert at kalde, selvom der ikke er noget at afvise. Returnerer `409`, hvis jobbet stadig er `queued` eller `processing` (du kan ikke afvise en kørsel, der ikke er færdig).

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

**Svar**

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

### Generer ofte stillede spørgsmål (FAQ) fra uploadede dokumenter

`POST /faqs/generate-from-documents`

Læser et eller flere dokumenter, der allerede findes i din kontos filarkiv, og får AI'en til at udarbejde FAQ'er ud fra deres indhold. Den tjekker udkastene mod dit eksisterende bibliotek, så den genbruger eller opdaterer poster i stedet for at oprette dubletter. Resultaterne skrives **ikke** med det samme — de gemmes som et afventende ændringssæt på kampagnen, som du kan gennemse, og derefter anvende (eller kassere) med [Anvend gennemsete FAQ-ændringer](#apply-reviewed-faq-changes) nedenfor. Dette koster kreditter, da det er en AI-genereringsproces baseret på dokumentteksten.

Dette slutpunkt medfører ikke filen: `storagePath` skal pege på en fil, der allerede findes i din egen upload-mappe (`users/{your user id}/uploads/`), samme konvention som [Importer et uploadet dokument](knowledge-base.md#import-an-uploaded-document) i vidensbase-API'et.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `campaignId` | Ja | Kampagnen, som de genererede FAQ'er foreslås til. |
| `uploadedFiles` | Ja | Ikke-tomt array af filer, der skal læses, hver `{ storagePath, fileName, mimeType }`. `storagePath` skal starte med `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()
```

**Svar** — `202 Accepted`

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

`faqCount` er det samlede antal foreslåede ændringer, der venter på gennemgang; `reusedCount`, `modifiedCount` og `newCount` opdeler dette i FAQ'er, der matchede en eksisterende post uændret, dem AI'en foreslår at redigere, og helt nye. Uploadede filer slettes fra lageret, når behandlingen er færdig, uanset om den lykkes eller ej.

### Anvend gennemsete FAQ-ændringer

`POST /faqs/apply-optimization`

Anvender (eller kasserer) et afventende sæt af AI-foreslåede FAQ-ændringer — den type, der produceres af [Generer FAQ'er fra dokumenter](#generate-faqs-from-uploaded-documents) ovenfor, eller af dashboardets FAQ-optimeringsgennemgang. Du vælger præcis, hvilke foreslåede ændringer der skal accepteres; alt, hvad du ikke nævner, forbliver uberørt (en udeladt ændring behandles aldrig som en afvisning, der sletter noget).

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `campaignId` | En af disse to | Kampagnen, hvis afventende FAQ-ændringer anvendes. |
| `agentId` | En af disse to | AI-agenten, hvis afventende FAQ-ændringer anvendes, på en agent-baseret konto. Angiv præcis én af `campaignId` / `agentId`, aldrig begge. |
| `acceptedChanges` | Ja | Array af de ændringer, du accepterer, hver `{ action, faq_id?, faq_ref_path?, question?, answer?, edit_scope? }`. `action` er en af `keep`, `remove`, `add_from_library`, `create_new`, `modify`. Send et tomt array for at kassere det afventende sæt uden at anvende noget. |

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

**Svar**

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

`faq_count` er kampagnens (eller agentens) samlede antal linkede FAQ'er efter anvendelse. Hvis der ikke var noget afventende ændringssæt at anvende, er svaret `{ "success": true, "message": "No pending FAQ changes to apply" }`.

### Find FAQ'er, der ligner en opgave

`POST /faqs/similar-for-task`

Rangerer dit FAQ-bibliotek efter relevans i forhold til spørgsmålet i en videnskløft-opgave — det samme opslag, der ligger bag dashboardets "Brug en eksisterende FAQ"-vælger. Skrivebeskyttet. `taskId` skal pege på en opgave af typen `faq_update`.

Dette slutpunkt svarer altid `200`, selv ved en forventet fejl som f.eks. en ukendt opgave — tjek `success` i brødteksten i stedet for HTTP-status.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `taskId` | Ja | Den `faq_update` opgave, der skal findes match til. |
| `limit` | Nej | Maksimalt antal match, der skal returneres. Standard er 20, begrænset til 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()
```

**Svar**

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

Match sorteres efter `similarity` (semantisk match når tilgængelig, ellers søgeordsoverlap), med det bedste først. Ved en blød fejl er formen `{ "success": false, "error": "...", "error_code": 404 }` — `error_code` afspejler, hvad HTTP-statussen normalt ville være.

### Løs en opgave med en eksisterende FAQ

`POST /faqs/resolve-task`

Løser en videnskløft-opgave ved at linke den til en FAQ, du allerede har (i stedet for at skrive en ny), sender den pågældende FAQ's svar til kontakten, der udløste kløften, og markerer opgaven som fuldført. Brug denne, efter at [Find FAQ'er, der ligner en opgave](#find-faqs-similar-to-a-task) har fundet en eksisterende FAQ, der allerede dækker spørgsmålet.

Ligesom slutpunktet ovenfor svarer dette altid `200` — tjek `success` i brødteksten.

**Anmodningsfelter**

| Felt | Påkrævet | Beskrivelse |
|---|---|---|
| `taskId` | Ja | Den `faq_update` opgave, der skal løses. |
| `faqId` | Ja | Den eksisterende FAQ, der skal linkes og sendes som svar. |

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

**Svar**

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

`follow_up_status` fortæller dig, hvad der skete med opfølgningen på kontakten: `published` (sendt med det samme), `queued` (AI'en var allerede i gang med at svare den kontakt, så den sendes næste gang), `skipped_no_contact` (opgaven har ingen linket kontakt), eller `skipped_no_campaign` (ingen kampagne at sende den igennem).

---

## FAQ API-fejl

FAQ-endpoints returnerer standardfejl-konvolutten:

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

| Status | Hvornår det sker på et FAQ-slutpunkt |
|---|---|
| `400` | Et påkrævet felt mangler eller er ugyldigt (f.eks. en tom `question`, en manglende `campaign_id` eller mere end 500 elementer i en bulk-anmodning). |
| `404` | FAQ'en eller kampagnen blev ikke fundet — enten eksisterer den ikke, eller også tilhører den en anden konto. |
| `409` | `POST /faqs/dedupe` blev kaldt, mens et dedupe-job allerede er `queued`/`processing`, eller `POST /faqs/dedupe/dismiss` blev kaldt, mens jobbet ikke er færdigt endnu. |

De delte koder, som ethvert endpoint kan returnere — `401`, `403` (din plan inkluderer ikke API-adgang), `429` (rate limit) og `500` — er angivet med vejledning om genforsøg i [Errors & Pagination](errors-and-pagination.md).

`POST /faqs/similar-for-task` og `POST /faqs/resolve-task` er de to undtagelser på denne side: de svarer `200` selv ved en forventet fejl (ukendt opgave, forkert opgavetype) og placerer i stedet den rigtige status i brødtekstens `error_code` — se hvert slutpunkt ovenfor.

---

## Relateret

- [Kampagne-API](campaigns.md) — de kampagner, dine FAQ'er er linket til.
- [Vidensbase-API](knowledge-base.md) — importér automatisk websteder og dokumenter til FAQ'er, og saml FAQ'er i genanvendelige vidensgrupper.
- [API-adgang](../integrations/api-access.md) — generér din API-nøgle.
- [Godkendelse](authentication.md) — alle måder at angive din nøgle på.
