
# API de preguntas frecuentes (FAQs)

Las preguntas frecuentes (FAQs) son las entradas de preguntas y respuestas que utiliza tu bot de IA al responder a los clientes. Cada FAQ pertenece a tu cuenta y puede vincularse a una o más campañas, de modo que la misma respuesta pueda reutilizarse en cualquier lugar donde sea relevante. La API de FAQs te permite gestionar esa biblioteca mediante programación: crear, actualizar, importar de forma masiva, reordenar y vincular FAQs a campañas desde tu propio código.

Todos los endpoints a continuación son relativos a la URL base `https://api.youraiconnector.com/v1`. Cada solicitud debe estar autenticada; consulta [Acceso a la API](../integrations/api-access.md) y [Autenticación](authentication.md). El acceso a la API es una función de pago; sin ella, las solicitudes se rechazan con un `403`.

> **Cómo utiliza el bot una FAQ:** Cuando creas o cambias una FAQ, la plataforma prepara sus datos de búsqueda (utilizados para hacer coincidir la FAQ con las preguntas entrantes) en segundo plano. Esto suele completarse en unos pocos segundos, tras lo cual el bot comienza a utilizar la entrada automáticamente.


---

## El objeto FAQ

Cada FAQ que devuelve la API tiene esta estructura:

| Campo | Tipo | Descripción |
|---|---|---|
| `id` | string | Identificador único de la pregunta frecuente (FAQ). |
| `question` | string | La pregunta del cliente que responde esta entrada. |
| `answer` | string | La respuesta que da el bot de IA. |
| `category` | string \| null | Etiqueta de categoría opcional de formato libre. |
| `tags` | string[] | Etiquetas opcionales para organizar las preguntas frecuentes. |
| `is_active` | boolean | Indica si el bot tiene permiso para usar esta pregunta frecuente. El valor predeterminado es `true`. |
| `is_global` | boolean | Marca la pregunta frecuente como no vinculada a una campaña o agente específico. Esto no hace que la pregunta frecuente se aplique en todas partes: una pregunta frecuente solo es utilizada por las campañas y agentes a los que está vinculada. El valor predeterminado es `false`. |
| `usage_count` | integer | Cantidad de veces que esta pregunta frecuente se ha utilizado en las respuestas de la IA. |
| `order_index` | integer | Posición de visualización de esta pregunta frecuente dentro de su campaña. |
| `campaign_ids` | string[] | IDs de las campañas a las que está vinculada esta pregunta frecuente. |
| `created_at` | string \| null | Marca de tiempo ISO 8601 de cuándo se creó la pregunta frecuente. |
| `updated_at` | string \| null | Marca de tiempo ISO 8601 del último cambio. |

Los campos que puedes **establecer** son: `question`, `answer`, `is_active`, `is_global`, `category`, `tags` y `order_index`. La plataforma gestiona todo lo demás (datos de búsqueda, recuentos de uso, marcas de tiempo); cualquier otro campo en el cuerpo de tu solicitud será ignorado.

---

## Listar FAQs

`GET /faqs`

Devuelve las FAQs de tu cuenta, empezando por las más recientes. Opcionalmente, filtra por una sola campaña o por estado activo.

**Parámetros de consulta**

| Parámetro | Requerido | Descripción |
|---|---|---|
| `campaign_id` | No | Solo devuelve las FAQs vinculadas a esta campaña. |
| `is_active` | No | Solo devuelve las FAQs con este estado activo (`true` o `false`). Este filtro se aplica por página, por lo que una página puede contener menos elementos que `limit`. |
| `limit` | No | Máximo de FAQs por página. El valor predeterminado es `50`, el máximo es `100`. |
| `cursor` | No | Un ID de FAQ para continuar después. Pasa el valor `next_cursor` de la página anterior. |

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

**Respuesta**

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

Cuando `next_cursor` es `null`, no hay más resultados.

---

## Obtener una pregunta frecuente

`GET /faqs/{faqId}`

Devuelve una única pregunta frecuente por su 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"]
```

**Respuesta**

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

---

## Crear una pregunta frecuente

`POST /faqs`

Crea una nueva pregunta frecuente y la vincula a una campaña.

**Campos de la solicitud**

| Campo | Requerido | Descripción |
|---|---|---|
| `campaign_id` | Sí | La campaña a la que vincular la nueva pregunta frecuente. |
| `question` | Sí | La pregunta del cliente que responde esta entrada. |
| `answer` | Sí | La respuesta que debe dar el bot. |
| `is_active` | No | Si el bot puede usar esta pregunta frecuente. El valor predeterminado es `true`. |
| `is_global` | No | Si la pregunta frecuente se aplica a todas las campañas. El valor predeterminado es `false`. |
| `category` | No | Una etiqueta de categoría de formato libre. |
| `tags` | No | Una matriz de etiquetas. |
| `order_index` | No | Posición de visualización dentro de la campaña. El valor predeterminado es `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"]
```

**Respuesta**

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

---

## Actualizar una pregunta frecuente

`PUT /faqs/{faqId}`

Actualiza parcialmente una pregunta frecuente. Solo se modifican los campos editables proporcionados; todo lo demás mantiene su valor actual. Cambiar `question` o `answer` actualiza automáticamente los datos de búsqueda de la pregunta frecuente en segundo plano.

Si envía `question` o `answer`, deben ser cadenas no vacías. Si no se envían campos editables reconocidos, se devuelve un `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()
```

**Respuesta**

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

---

## Eliminar una pregunta frecuente

`DELETE /faqs/{faqId}`

Elimina permanentemente una pregunta frecuente. Opcionalmente, pase `campaign_id` como parámetro de consulta para eliminar también la pregunta frecuente de la lista de preguntas frecuentes de esa campaña.

**Parámetros de consulta**

| Parámetro | Obligatorio | Descripción |
|---|---|---|
| `campaign_id` | No | Elimina también las preguntas frecuentes de la lista de preguntas frecuentes de esta campaña. |

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

**Respuesta**

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

---

## Eliminación masiva de preguntas frecuentes

`POST /faqs/bulk-delete`

Elimina hasta 500 preguntas frecuentes en una sola solicitud. Cuando se proporciona `campaign_id`, las preguntas frecuentes eliminadas también se retiran de la lista de preguntas frecuentes de esa campaña.

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `faq_ids` | Sí | Una matriz no vacía de IDs de preguntas frecuentes a eliminar (máx. 500). |
| `campaign_id` | No | Elimina también las preguntas frecuentes eliminadas de la lista de preguntas frecuentes de esta campaña. |

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

**Respuesta**

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

---

## Preguntas frecuentes sobre importación

`POST /faqs/import`

Importa en bloque hasta 500 preguntas frecuentes y las vincula todas a una campaña. Los elementos cuyo `question` coincida con una pregunta frecuente existente en su biblioteca (sin distinguir entre mayúsculas y minúsculas) **actualizan** dicha pregunta frecuente en lugar de crear un duplicado.

> **Consejo de rendimiento:** La búsqueda de duplicados analiza toda su biblioteca de preguntas frecuentes, por lo que las bibliotecas muy grandes ralentizan las importaciones. Es preferible realizar menos importaciones de mayor tamaño que muchas pequeñas.

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `campaign_id` | Sí | La campaña a la que se vinculan todas las preguntas frecuentes importadas. |
| `faqs` | Sí | Una matriz no vacía de elementos de preguntas frecuentes (máx. 500). Cada elemento debe tener un `question` y un `answer` no vacíos; también puede incluir `is_active`, `is_global`, `category`, `tags` y `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()
```

**Respuesta**

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

`faq_ids` son los ID de las preguntas frecuentes creadas o actualizadas, en el orden en que los proporcionó.

---

## Reordenar preguntas frecuentes

`POST /faqs/reorder`

Establece el orden de visualización de las preguntas frecuentes de una campaña. Proporcione la lista **completa** de los ID de las preguntas frecuentes en el orden deseado; la posición de cada pregunta frecuente se actualiza para coincidir con su lugar en la matriz.

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `campaign_id` | Sí | La campaña cuyas preguntas frecuentes se están reordenando. |
| `ordered_faq_ids` | Sí | Una matriz no vacía de todos los ID de preguntas frecuentes de la campaña en el orden de visualización deseado (máximo 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()
```

**Respuesta**

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

Si la campaña o cualquiera de los ID de las preguntas frecuentes no se encuentran en su cuenta, la solicitud devuelve `404 One or more FAQs were not found`.

---

## Vincular una pregunta frecuente a una campaña

`POST /faqs/{faqId}/link`

Vincula una pregunta frecuente existente a una campaña adicional. Una pregunta frecuente puede ser compartida por cualquier número de campañas, por lo que la misma respuesta solo necesita mantenerse una vez.

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `campaign_id` | Sí | La campaña a la que vincular la pregunta frecuente. |

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

**Respuesta**

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

---

## Desvincular una FAQ de una campaña

`POST /faqs/{faqId}/unlink`

Elimina una FAQ de una campaña sin borrar la FAQ en sí. La FAQ permanece en su biblioteca y sigue vinculada a cualquier otra campaña.

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `campaign_id` | Sí | La campaña de la que se eliminará la 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()
```

**Respuesta**

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

---

## Reconstruir los datos de búsqueda de una FAQ

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

Pone en cola una reconstrucción de los datos que utiliza el bot de IA para encontrar esta FAQ (sus datos de búsqueda semántica y por palabras clave). Esto es útil si una FAQ no aparece en las respuestas como se esperaba. La reconstrucción se ejecuta en segundo plano y suele completarse en unos segundos; es posible que la FAQ se excluya temporalmente de las respuestas de la IA mientras se reconstruye.

Este endpoint devuelve `202 Accepted` porque el trabajo continúa después de que se envía la respuesta. El `status` es siempre `"processing"`: vuelva a consultar la FAQ más tarde si necesita confirmar que se ha completado.

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

**Respuesta**

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

---

## Gestión de preguntas frecuentes asistida por IA

Los endpoints a continuación van más allá del CRUD simple: llaman a las mismas herramientas de asistencia por IA que utiliza el editor de preguntas frecuentes del panel de control, lo que permite encontrar duplicados, generar entradas a partir de un documento y relacionar preguntas frecuentes con tareas abiertas de brechas de conocimiento. Los cuerpos de las solicitudes en este conjunto utilizan nombres de campo `camelCase` (`campaignId`, `taskId`, `sourceIds`...), que coinciden con las estructuras de solicitud de la propia aplicación, en lugar de los `snake_case` utilizados en otras partes de esta página; copie los ejemplos a continuación en lugar de intentar adivinar un nombre de campo.

### Bifurcar una pregunta frecuente en una copia exclusiva para una campaña

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

Crea una nueva pregunta frecuente que es una copia de una existente, limitada a una sola campaña, y vuelve a vincular esa campaña a la nueva copia en lugar de a la original. Utilice esto cuando desee personalizar una respuesta para una campaña sin cambiarla en todos los demás lugares donde se utiliza la pregunta frecuente original. La pregunta frecuente original permanece en su lugar; solo pierde el enlace de esta campaña.

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `campaign_id` | Sí | La campaña a la que se limitará la nueva copia y desde la cual se volverá a vincular la pregunta frecuente original. |
| `question` | Sí | La pregunta para la nueva copia específica de la campaña. |
| `answer` | Sí | La respuesta para la nueva copia específica de la campaña. |

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

**Respuesta** — `201 Created`

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

### Encontrar preguntas frecuentes casi duplicadas

`POST /faqs/dedupe`

Inicia un trabajo en segundo plano que escanea su biblioteca de preguntas frecuentes en busca de entradas casi duplicadas o superpuestas y las fusiona o elimina cuando tiene confianza en el resultado. Es útil después de una importación masiva o después de que varias rondas de preguntas frecuentes generadas por IA hayan dejado la biblioteca con superposiciones. Solo se puede ejecutar un trabajo de deduplicación por cuenta a la vez; iniciar un segundo mientras un trabajo aún se está ejecutando devuelve `409`.

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `sourceIds` | No | Matriz de ID de fuentes de base de conocimiento para limitar la deduplicación. Omítalo para escanear toda su biblioteca de preguntas frecuentes. |

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

**Respuesta** — `202 Accepted`

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

El trabajo se ejecuta en segundo plano y normalmente tarda unos minutos en una biblioteca grande. No hay un endpoint de estado independiente; vuelva a consultar [`GET /faqs`](#list-faqs) después de una breve espera para ver qué ha cambiado. Cuando termine de revisar el resultado, llame al endpoint de descartar a continuación para borrarlo.

### Descartar un resultado de verificación de duplicados

`POST /faqs/dedupe/dismiss`

Borra el trabajo de deduplicación finalizado para que deje de mostrarse como un resultado activo. Es idempotente: es seguro llamarlo incluso si no hay nada que descartar. Devuelve `409` si el trabajo aún está `queued` o `processing` (no puede descartar una ejecución que no ha terminado).

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

**Respuesta**

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

### Generar preguntas frecuentes a partir de documentos cargados

`POST /faqs/generate-from-documents`

Lee uno o más documentos que ya se encuentren en el almacenamiento de archivos de su cuenta y hace que la IA redacte preguntas frecuentes a partir de su contenido, comparando los borradores con su biblioteca existente para reutilizar o actualizar entradas en lugar de crear duplicados. Los resultados **no** se escriben de inmediato; se almacenan como un conjunto de cambios pendientes en la campaña para que usted los revise, y luego se aplican (o descartan) con [Aplicar cambios de preguntas frecuentes revisados](#apply-reviewed-faq-changes) a continuación. Esto consume créditos, ya que es una pasada de generación de IA sobre el texto del documento.

Este endpoint no transporta el archivo: `storagePath` debe apuntar a un archivo que ya esté en su propia carpeta de cargas (`users/{your user id}/uploads/`), siguiendo la misma convención que [Importar un documento cargado](knowledge-base.md#import-an-uploaded-document) en la API de la Base de conocimientos.

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `campaignId` | Sí | La campaña para la que se proponen las preguntas frecuentes generadas. |
| `uploadedFiles` | Sí | Matriz no vacía de archivos para leer, cada uno `{ storagePath, fileName, mimeType }`. `storagePath` debe comenzar con `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()
```

**Respuesta** — `202 Accepted`

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

`faqCount` es el número total de cambios propuestos a la espera de revisión; `reusedCount`, `modifiedCount` y `newCount` desglosan eso en preguntas frecuentes que coincidieron con una entrada existente sin cambios, aquellas que la IA propone editar y las completamente nuevas. Los archivos cargados se eliminan del almacenamiento una vez que finaliza el procesamiento, independientemente de si tiene éxito o no.

### Aplicar cambios de preguntas frecuentes revisados

`POST /faqs/apply-optimization`

Aplica (o descarta) un conjunto pendiente de cambios de preguntas frecuentes propuestos por la IA, del tipo producido por [Generar preguntas frecuentes a partir de documentos](#generate-faqs-from-uploaded-documents) arriba, o por la revisión de optimización de preguntas frecuentes del panel de control. Usted elige exactamente qué cambios propuestos aceptar; todo lo que no mencione se deja intacto (un cambio omitido nunca se trata como un rechazo que elimine algo).

**Campos de la solicitud**

| Campo | Obligatorio | Descripción |
|---|---|---|
| `campaignId` | Uno de estos dos | La campaña cuyos cambios pendientes de preguntas frecuentes se están aplicando. |
| `agentId` | Uno de estos dos | El Agente de IA cuyos cambios pendientes de preguntas frecuentes se están aplicando, en una cuenta nativa del agente. Proporcione exactamente uno de `campaignId` / `agentId`, nunca ambos. |
| `acceptedChanges` | Sí | Matriz de los cambios que acepta, cada uno `{ action, faq_id?, faq_ref_path?, question?, answer?, edit_scope? }`. `action` es uno de `keep`, `remove`, `add_from_library`, `create_new`, `modify`. Envíe una matriz vacía para descartar el conjunto pendiente sin aplicar nada. |

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

**Respuesta**

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

`faq_count` es el recuento total de preguntas frecuentes vinculadas de la campaña (o del Agente) después de la aplicación. Si no había ningún conjunto de cambios pendiente que aplicar, la respuesta es `{ "success": true, "message": "No pending FAQ changes to apply" }`.

### Encontrar preguntas frecuentes similares a una tarea

`POST /faqs/similar-for-task`

Clasifica su biblioteca de preguntas frecuentes por relevancia respecto a la pregunta de una tarea de brecha de conocimiento; es la misma búsqueda que utiliza el selector "Usar una pregunta frecuente existente" del panel de control. Solo lectura. `taskId` debe apuntar a una tarea de tipo `faq_update`.

Este endpoint siempre responde `200`, incluso ante un fallo esperado como una tarea desconocida; compruebe `success` en el cuerpo en lugar del estado HTTP.

**Campos de la solicitud**

| Campo | Requerido | Descripción |
|---|---|---|
| `taskId` | Sí | La tarea `faq_update` para encontrar coincidencias. |
| `limit` | No | Número máximo de coincidencias a devolver. El valor predeterminado es 20, con un límite de 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()
```

**Respuesta**

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

Las coincidencias se ordenan por `similarity` (coincidencia semántica cuando está disponible, superposición de palabras clave en caso contrario), primero las mejores. En un fallo leve, la forma es `{ "success": false, "error": "...", "error_code": 404 }`; `error_code` refleja lo que sería normalmente el estado HTTP.

### Resolver una tarea con una FAQ existente

`POST /faqs/resolve-task`

Resuelve una tarea de brecha de conocimiento vinculándola a una FAQ que ya tiene (en lugar de escribir una nueva), envía la respuesta de esa FAQ al contacto que provocó la brecha y marca la tarea como completada. Utilice esto después de que [Buscar FAQs similares a una tarea](#find-faqs-similar-to-a-task) encuentre una FAQ existente que ya cubra la pregunta.

Al igual que el endpoint anterior, este siempre responde `200`; compruebe `success` en el cuerpo.

**Campos de la solicitud**

| Campo | Requerido | Descripción |
|---|---|---|
| `taskId` | Sí | La tarea `faq_update` a resolver. |
| `faqId` | Sí | La FAQ existente para vincular y enviar como respuesta. |

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

**Respuesta**

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

`follow_up_status` le indica qué sucedió con el seguimiento del contacto: `published` (enviado de inmediato), `queued` (la IA ya estaba respondiendo a ese contacto, por lo que se enviará a continuación), `skipped_no_contact` (la tarea no tiene un contacto vinculado) o `skipped_no_campaign` (no hay ninguna campaña a través de la cual enviarlo).

---

## Errores de la API de preguntas frecuentes

Los endpoints de preguntas frecuentes devuelven el sobre de error estándar:

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

| Estado | Cuándo ocurre en un endpoint de FAQ |
|---|---|
| `400` | Falta un campo obligatorio o no es válido (por ejemplo, un `question` vacío, un `campaign_id` faltante o más de 500 elementos en una solicitud masiva). |
| `404` | No se encontró la FAQ o la campaña; o bien no existe o pertenece a otra cuenta. |
| `409` | Se llamó a `POST /faqs/dedupe` mientras un trabajo de deduplicación ya estaba `queued`/`processing`, o se llamó a `POST /faqs/dedupe/dismiss` mientras el trabajo aún no había terminado. |

Los códigos compartidos que puede devolver cualquier endpoint — `401`, `403` (su plan no incluye acceso a la API), `429` (límite de tasa) y `500` — se enumeran con orientación sobre reintentos en [Errores y paginación](errors-and-pagination.md).

`POST /faqs/similar-for-task` y `POST /faqs/resolve-task` son las dos excepciones en esta página: responden `200` incluso ante un fallo esperado (tarea desconocida, tipo de tarea incorrecto) y colocan el estado real en el `error_code` del cuerpo; consulte cada endpoint anterior.

---

## Relacionado

- [API de campañas](campaigns.md): las campañas a las que están vinculadas sus FAQs.
- [API de base de conocimientos](knowledge-base.md): importe sitios web y documentos a FAQs automáticamente y agrupe FAQs en grupos de conocimiento reutilizables.
- [Acceso a la API](../integrations/api-access.md): genere su clave de API.
- [Autenticación](authentication.md): todas las formas de proporcionar su clave.
