> ## Documentation Index
> Fetch the complete documentation index at: https://woku.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Guía de Integración API

> Documentación completa de la API v1 de woku: wokus, NPS, CSAT, CES, formularios, flows, invitaciones, capturas del SDK móvil, trackers externos, destinos de tickets y planes de acción

<Info>
  **Disponible en el plan Corporate.** Esta funcionalidad forma parte de las capacidades empresariales de woku. [Conversa con nuestro equipo comercial](https://woku.app/pricing).
</Info>

Esta guía te muestra cómo integrar la API de **woku** en tus propias aplicaciones para automatizar la creación de **wokus**, capturar respuestas de todas las herramientas de feedback, enviar encuestas por correo o WhatsApp y enriquecer tus datos con identificadores de tus sistemas internos.

<Note>
  Esta guía aplica **solo a la versión v1** de la API. Corresponde exactamente a la pestaña **API Reference** que ves en la parte superior de la documentación. Para el comportamiento histórico (rutas sin el prefijo `/v1/`), cambia el selector de versión a **v0**.
</Note>

## Referencia interactiva

Toda la API está documentada de forma interactiva en la pestaña **API Reference** de esta misma documentación. Allí puedes ver cada endpoint con su esquema completo, ejecutarlo en vivo desde el navegador y copiar ejemplos en tu lenguaje preferido.

Esta guía es complementaria: te explica el flujo y el "por qué" de cada llamada para que la integración te tome el menor tiempo posible.

## Introducción

La API de woku te permite operar de forma programática todas las herramientas de feedback: **wokus**, **NPS**, **CSAT**, **CES**, **formularios** y **flows**, además de **invitaciones**, **cuarentenas**, **trackers externos**, **destinos de tickets** y **planes de acción**. Los casos de uso típicos:

1. **Crear wokus automáticamente** cuando ocurre un evento en tu aplicación (por ejemplo, cierre de una venta, fin de una sesión de capacitación, entrega de un producto).
2. **Capturar respuestas** directamente desde tu interfaz o backend, sin redirigir al cliente al sitio de woku: calificaciones y reseñas de wokus, puntajes NPS, CSAT y CES, y respuestas de formularios.
3. **Enviar encuestas por correo o WhatsApp** a listas de destinatarios desde tu backend.
4. **Capturar feedback desde tu app móvil** con un único endpoint idempotente, pensado para el SDK de React Native.
5. **Etiquetar herramientas** con identificadores de tus sistemas (CRM, ERP, ticketing) para luego buscarlas cruzadas.
6. **Extraer respuestas y reportes** hacia tus dashboards y sistemas internos.
7. **Configurar a dónde llegan los tickets de soporte** (Zendesk, Salesforce, Slack, un servicio propio o correo) y cómo se enrutan según tus trackers.
8. **Gestionar planes de acción generados por IA**: aprobarlos, enviarlos a Jira, monday.com, ClickUp o Notion, o trabajarlos dentro de woku.

Todas las llamadas son HTTP con JSON o `multipart/form-data` y responden con estructuras consistentes.

### URL Base

```
https://clientapi.woku.app
```

Todos los endpoints v1 viven bajo el prefijo `/v1/`.

### Autenticación

Cada empresa registrada en woku tiene una **Clave de Compañía** que se incluye en el header `Authorization` de cada solicitud:

```
Authorization: Bearer <Company-Key>
```

La clave la obtiene el propietario de la empresa desde la sección **Información** de la empresa, en la aplicación administrativa: [https://admin.woku.app](https://admin.woku.app/)

<img src="https://mintcdn.com/woku/4zVAR1XAPSMsEpMD/images/api-clave-empresa.png?fit=max&auto=format&n=4zVAR1XAPSMsEpMD&q=85&s=9577faa826819f297e9559f4bd97837a" alt="Interfaz de la aplicación woku en la sección de 'Información' de la empresa. Se muestran los datos de la compañía, incluyendo el logo, nombre, página web y enlaces a redes sociales como Instagram, Facebook, LinkedIn, X (ex Twitter), TikTok y GitHub. En la parte inferior, se destaca la opción 'Obtener Clave', que permite copiar la clave de la compañía para clientapi.woku.app." className="mx-auto" width="3840" height="2160" data-path="images/api-clave-empresa.png" />

<div style={{ textAlign: 'center', fontSize: '0.875rem', color: '#6b7280' }}><strong>Vista de la ventana de información de la empresa.</strong></div>

<Tip>
  La misma clave que usabas en v0 funciona para v1. No necesitas regenerarla para migrar.
</Tip>

## Compañía

### Obtener la compañía actual

Recupera los datos de la empresa asociada a la Clave de Compañía. Útil para validar que tu integración esté autenticando con la cuenta correcta.

**Endpoint**

```
GET /v1/companies/me
```

**Headers**

* `Authorization: Bearer <Company-Key>`

**Respuestas**

* `200 OK`: Devuelve el objeto de la compañía.
* `403 Forbidden`: La clave no es válida o no tiene permiso.

## Wokus

Las funciones de **wokus** te permiten crear, consultar, calificar y compartir wokus de forma programática.

### Crear un woku con URL de archivo

Crea un woku usando una imagen o video alojado en una URL pública (CDN, S3, ImageKit, etc.).

**Endpoint**

```
POST /v1/wokus
```

**Headers**

* `Content-Type: application/json`
* `Authorization: Bearer <Company-Key>`

**Body**

```json theme={null}
{
  "description": "Customer Service Experience - Store #123",
  "fileUrl": "https://cdn.example.com/images/product-image.jpg",
  "folderSecondaryKey": "store-123",
  "parentFolderSecondaryKey": "region-north",
  "clientEmail": "customer@example.com",
  "clientPhone": 56912345678
}
```

**Campos**

* `description` *(string, obligatorio)*: Entre 3 y 140 caracteres.
* `fileUrl` *(string, obligatorio)*: URL pública de una imagen o video (preferentemente `.mp4`).
* `folderSecondaryKey` *(string, opcional)*: Clave de la carpeta donde se almacena el woku.
* `parentFolderSecondaryKey` *(string, opcional)*: Clave de la carpeta padre. No puede ser la misma que `folderSecondaryKey`.
* `clientEmail` *(string, opcional)*: Email del cliente asociado.
* `clientPhone` *(number, opcional)*: Número de WhatsApp.

**Respuestas**

* `201 Created`: Devuelve el objeto del woku creado.
* `400 Bad Request`: El body no pasó la validación.
* `403 Forbidden`: Clave inválida.

### Crear un woku con archivo (multipart)

Cuando el archivo está en tu propio storage o tu cliente sube la imagen directamente desde un formulario, usa el endpoint con `multipart/form-data`.

**Endpoint**

```
POST /v1/wokus/form-data
```

**Headers**

* `Content-Type: multipart/form-data`
* `Authorization: Bearer <Company-Key>`

**Form fields**

* `file` *(binary, obligatorio)*: Archivo de imagen o video.
* `description` *(string, obligatorio)*: Entre 3 y 140 caracteres.
* `folderSecondaryKey` *(string, opcional)*.
* `parentFolderSecondaryKey` *(string, opcional)*.
* `clientEmail` *(string, opcional)*.
* `clientPhone` *(string, opcional)*.

**Respuestas**

* `201 Created`: Woku creado.
* `400 Bad Request`: Validación fallida (formato de archivo, tamaño, campos requeridos).
* `403 Forbidden`: Clave inválida.

### Obtener los datos de un woku

Recupera el woku junto con sus calificaciones y reseñas.

**Endpoint**

```
GET /v1/wokus/:id/review
```

**Headers**

* `Authorization: Bearer <Company-Key>`

**Path params**

* `id` *(string, obligatorio)*: Identificador del woku.

**Respuestas**

* `200 OK`: Objeto del woku con sus reseñas.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe un woku con ese ID en tu empresa.

### Listar los wokus de la empresa

Lista paginada de los wokus de tu empresa.

**Endpoint**

```
GET /v1/wokus
```

**Query params**

* `page` *(number, opcional)*: Por defecto 1.
* `limit` *(number, opcional)*: Por defecto 20.

**Respuestas**

* `200 OK`: Lista paginada de wokus.
* `403 Forbidden`: Clave inválida.

### Obtener un woku con estadísticas

Devuelve un woku con sus estadísticas agregadas de reseñas.

**Endpoint**

```
GET /v1/wokus/:id
```

**Respuestas**

* `200 OK`: Woku con estadísticas.
* `403 Forbidden`: El woku pertenece a otra empresa.
* `404 Not Found`: No existe el woku.

### Listar las reseñas de un woku

Reseñas en texto y audio, paginadas, de la más reciente a la más antigua. Las reseñas de audio exponen su transcripción. Nunca se incluyen datos de contacto del cliente, solo `clientId`. Cada reseña incluye `responseChannel`: el canal por el que llegó (`whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`, o un valor personalizado definido por ti); es `null` en respuestas históricas.

**Endpoint**

```
GET /v1/wokus/:id/reviews
```

**Query params**

* `page` *(number, opcional)*.
* `limit` *(number, opcional)*.

**Respuestas**

* `200 OK`: Lista paginada de reseñas.
* `403 Forbidden`: El woku pertenece a otra empresa.
* `404 Not Found`: No existe el woku.

### Calificar con una reseña en texto

Agrega una calificación de 1 a 5 estrellas con un comentario escrito.

**Endpoint**

```
POST /v1/wokus/:id/textnotes
```

**Headers**

* `Content-Type: application/json`
* `Authorization: Bearer <Company-Key>`

**Body**

```json theme={null}
{
  "qualification": 5,
  "description": "Excellent service! The staff was very helpful.",
  "clientEmail": "customer@example.com",
  "clientPhone": 56912345678,
  "anonymous": false
}
```

**Campos**

* `qualification` *(integer, obligatorio)*: Valor entre 1 y 5.
* `description` *(string, obligatorio)*: Reseña en texto, máximo 3000 caracteres.
* `clientEmail` *(string, opcional)*: Si no se entrega, `anonymous` debe ser `true`.
* `clientPhone` *(number, opcional)*.
* `anonymous` *(boolean, opcional)*: Por defecto `false`.
* `responseChannel` *(string, opcional, máx 48)*: Canal por el que llegó la respuesta. Valores canónicos: `whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`. También puedes enviar tu propio valor personalizado, que reemplaza el valor por defecto. Por defecto `api`.

**Respuestas**

* `201 Created`: Reseña registrada.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el woku.

### Calificar con una reseña en audio

Sube un archivo de audio junto con la calificación.

**Endpoint**

```
POST /v1/wokus/:id/voicemails
```

**Headers**

* `Content-Type: multipart/form-data`
* `Authorization: Bearer <Company-Key>`

**Form fields**

* `file` *(binary, obligatorio)*: Audio. Recomendado `.mp4` o `.wav`.
* `qualification` *(string `"1"`, `"5"`, obligatorio)*.
* `clientEmail` *(string, opcional)*.
* `clientPhone` *(string, opcional)*.
* `anonymous` *(string `"true"` o `"false"`, opcional)*.
* `responseChannel` *(string, opcional, máx 48)*: Canal por el que llegó la respuesta. Valores canónicos: `whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`. También puedes enviar tu propio valor personalizado, que reemplaza el valor por defecto. Por defecto `api`.

**Respuestas**

* `201 Created`: Reseña registrada.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el woku.

### Compartir un woku por correo

Envía el woku a uno o varios destinatarios. Es el mismo flujo que se inicia desde la interfaz de compartir.

**Endpoint**

```
POST /v1/wokus/:id/share
```

**Headers**

* `Content-Type: application/json`
* `Authorization: Bearer <Company-Key>`

**Body (destinatario único)**

```json theme={null}
{
  "clientEmail": "customer@example.com"
}
```

**Body (múltiples destinatarios)**

```json theme={null}
{
  "clientEmails": ["customer1@example.com", "customer2@example.com"]
}
```

<Tip>
  Debes enviar **uno u otro** de los dos campos, no ambos.
</Tip>

**Respuestas**

* `200 OK`: Envío encolado.
* `400 Bad Request`: Validación fallida (correo mal formado, ambos campos vacíos, etc.).
* `403 Forbidden`: Clave inválida.

## Capturas del SDK móvil

Endpoint único de ingesta pensado para el [SDK de React Native](/docs/development/sdk-react-native). Recibe una captura normalizada y la enruta según su `kind`: `woku` (reseña de un woku), `nps`, `csat` o `ces`. El canal de la respuesta queda sellado como `mobile-sdk` del lado del servidor.

**Endpoint**

```
POST /v1/captures
```

Acepta dos tipos de contenido:

* `application/json`: capturas de texto o calificación. El body es la captura.
* `multipart/form-data`: capturas con audio. El archivo va en el campo `file` y la captura como JSON en el campo `payload`. El audio está disponible para `woku` y `nps`; los comentarios de audio aún no están soportados para `csat` ni `ces`.

**Body (JSON)**

```json theme={null}
{
  "id": "3f2c9a2e-8b1d-4f6a-9c3e-1a2b3c4d5e6f",
  "kind": "woku",
  "targetId": "671000000000000000000001",
  "rating": 5,
  "comment": "Excelente atención en la tienda.",
  "respondent": { "email": "customer@example.com" }
}
```

**Campos**

* `id` *(string, opcional)*: Identificador generado por el cliente. Se usa como clave de idempotencia.
* `kind` *(string, obligatorio)*: `woku`, `nps`, `csat` o `ces`.
* `targetId` *(string)*: Id del woku (obligatorio para `woku`), del NPS Tool (opcional para `nps`) o del CSAT/CES Tool (obligatorio para `csat` y `ces`).
* `rating` *(integer, opcional)*: Calificación del woku, de 1 a 5.
* `score` *(integer, opcional)*: Puntaje NPS de 0 a 10, o CSAT/CES de 1 a 5.
* `comment` *(string, opcional, máx 3000)*: Comentario en texto. Obligatorio en las capturas de texto de un woku.
* `respondent` *(object, opcional)*: `email`, `phone`, `externalId`. Sin identificador, la captura queda anónima.

**Idempotencia**

La clave de idempotencia se toma del header `X-Woku-Idempotency-Key` o, en su ausencia, del campo `id` del body. Si el mismo envío se reintenta (por ejemplo, por la cola offline del SDK), la API devuelve el resultado ya registrado en lugar de crear un duplicado.

**Respuestas**

* `201 Created`: Captura aceptada. Devuelve `id`, `kind`, `remoteId` y `status: "accepted"`.
* `400 Bad Request`: Validación fallida (falta `targetId`, audio en `csat` o `ces`, `payload` malformado).
* `403 Forbidden`: Clave inválida.

## Respuestas NPS

Puedes capturar puntajes NPS y extraer las respuestas individuales hacia tus sistemas. Los reportes agregados están en la sección [Reportes NPS](#reportes-nps).

### Capturar un puntaje NPS

**Endpoint**

```
POST /v1/nps
```

**Body**

```json theme={null}
{
  "score": 9,
  "npsToolId": "671000000000000000000001",
  "clientEmail": "customer@example.com"
}
```

**Campos**

* `score` *(integer, obligatorio)*: De 0 a 10.
* `npsToolId` *(string, opcional)*: NPS Tool específico. Si se omite, el puntaje se registra a nivel de compañía.
* `clientEmail` *(string, opcional)*: Si no se entrega, la respuesta queda anónima.
* `anonymous` *(boolean, opcional)*.
* `responseChannel` *(string, opcional, máx 48)*: Canal por el que llegó la respuesta. Valores canónicos: `whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`. También puedes enviar tu propio valor personalizado, que reemplaza el valor por defecto. Por defecto `api`.

**Respuestas**

* `201 Created`: Devuelve `npsId` y el objeto de la respuesta.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: El NPS Tool no pertenece a tu empresa.

### Agregar una reseña a un puntaje NPS

Una vez creado el puntaje, puedes adjuntarle una reseña opcional en texto o audio.

**Endpoints**

```
POST /v1/nps/:id/textnotes
POST /v1/nps/:id/voicemails
```

* **Texto**: body JSON `{ "description": "..." }`, máximo 3000 caracteres.
* **Audio**: `multipart/form-data` con el archivo en el campo `file` (m4a, aac o mp4). La transcripción se genera en el servidor.

**Respuestas**

* `201 Created`: Reseña adjuntada.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: El NPS pertenece a otra empresa.
* `404 Not Found`: No existe el NPS.

### Consultar NPS Tools y respuestas

Endpoints de lectura para traer los datos NPS a tus sistemas:

* `GET /v1/nps-tools`: Lista paginada de los NPS Tools de tu empresa.
* `GET /v1/nps-tool/:id`: Definición de un NPS Tool (para construir la pregunta).
* `GET /v1/nps`: Respuestas paginadas, de la más reciente a la más antigua.
* `GET /v1/nps/:id`: Una respuesta individual.

`GET /v1/nps` acepta los filtros `npsToolId`, `from` y `to` (fechas ISO sobre la fecha de creación) y `withFeedback=true` (solo respuestas con reseña en texto o audio), además de `page` y `limit`. Las respuestas nunca incluyen datos de contacto del cliente, solo `clientId`. Cada respuesta incluye `responseChannel`: el canal por el que llegó (`whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`, o un valor personalizado definido por ti); es `null` en respuestas históricas.

## CSAT y CES

Las familias **CSAT** (satisfacción, escala 1 a 5) y **CES** (esfuerzo, escala 1 a 5) son espejos exactos. Los ejemplos usan `csat`; reemplaza `csat` por `ces` y `csatToolId` por `cesToolId` para obtener la familia CES. A diferencia de NPS, el id de la herramienta es siempre obligatorio: los CSAT y CES son siempre encuestas personalizadas.

### Capturar un puntaje CSAT

**Endpoint**

```
POST /v1/csat
```

**Body**

```json theme={null}
{
  "score": 4,
  "csatToolId": "671000000000000000000002",
  "clientEmail": "customer@example.com"
}
```

**Campos**

* `score` *(integer, obligatorio)*: De 1 a 5.
* `csatToolId` *(string, obligatorio)*: CSAT Tool que responde la encuesta.
* `clientEmail` *(string, opcional)*: Si no se entrega, la respuesta queda anónima.
* `anonymous` *(boolean, opcional)*.
* `responseChannel` *(string, opcional, máx 48)*: Canal por el que llegó la respuesta. Valores canónicos: `whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`. También puedes enviar tu propio valor personalizado, que reemplaza el valor por defecto. Por defecto `api`.

**Respuestas**

* `201 Created`: Devuelve `csatId` y el objeto de la respuesta.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: El CSAT Tool no pertenece a tu empresa.

### Agregar un comentario a una respuesta CSAT

**Endpoints**

```
POST /v1/csat/:id/textnotes
POST /v1/csat/:id/voicemails
```

* **Texto**: body JSON `{ "description": "..." }`, máximo 3000 caracteres.
* **Audio**: `multipart/form-data` con el archivo en el campo `file` (m4a, aac o mp4). La transcripción se genera en el servidor.

**Respuestas**

* `201 Created`: Comentario aceptado.
* `403 Forbidden`: La respuesta pertenece a otra empresa.
* `404 Not Found`: No existe la respuesta CSAT.

### Consultar tools y respuestas CSAT

* `GET /v1/csat-tools`: Lista paginada de los CSAT Tools de tu empresa.
* `GET /v1/csat-tool/:id`: Definición de un CSAT Tool (para construir la encuesta).
* `GET /v1/csat`: Respuestas paginadas, con los filtros `csatToolId`, `from`, `to` y `withFeedback`.
* `GET /v1/csat/:id`: Una respuesta individual.

Cada respuesta incluye `responseChannel`: el canal por el que llegó (`whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`, o un valor personalizado definido por ti); es `null` en respuestas históricas.

La familia CES expone exactamente los mismos endpoints: `POST /v1/ces`, `POST /v1/ces/:id/textnotes`, `POST /v1/ces/:id/voicemails`, `GET /v1/ces-tools`, `GET /v1/ces-tool/:id`, `GET /v1/ces` y `GET /v1/ces/:id`.

## Formularios

Consulta y captura de **formularios** desde tus propios sistemas: renderiza el formulario con su definición, envía respuestas y extrae los resultados.

### Listar los formularios de la empresa

**Endpoint**

```
GET /v1/forms
```

**Query params**

* `page` *(number, opcional)*.
* `limit` *(number, opcional)*.

**Respuestas**

* `200 OK`: Lista paginada de formularios.
* `403 Forbidden`: Clave inválida.

### Obtener la definición de un formulario

Devuelve el formulario activo (campos, configuración, contenido localizado) junto con el branding de la empresa, para renderizarlo en tu propia interfaz.

**Endpoint**

```
GET /v1/forms/:id
```

**Respuestas**

* `200 OK`: Definición del formulario.
* `403 Forbidden`: El formulario pertenece a otra empresa.
* `404 Not Found`: No existe o está inactivo.

### Enviar una respuesta de formulario

**Endpoint**

```
POST /v1/forms/:id/responses
```

**Body**

```json theme={null}
{
  "anonymous": false,
  "email": "respondent@example.com",
  "answers": { "field-uuid-1": "John Doe", "field-uuid-2": 5 }
}
```

**Campos**

* `anonymous` *(boolean, obligatorio)*.
* `email` / `phone` *(string, opcional)*: Requerido según cómo identifica clientes el formulario, cuando la respuesta no es anónima.
* `answers` *(object, obligatorio)*: Respuestas por id de campo. Se validan contra la definición del formulario.
* `responseChannel` *(string, opcional, máx 48)*: Canal por el que llegó la respuesta. Valores canónicos: `whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`. También puedes enviar tu propio valor personalizado, que reemplaza el valor por defecto. Por defecto `api`.

**Respuestas**

* `201 Created`: Respuesta registrada.
* `400 Bad Request`: Validación fallida o el formulario está cerrado.
* `403 Forbidden`: El formulario pertenece a otra empresa.
* `404 Not Found`: No existe o está inactivo.
* `429 Too Many Requests`: El respondente está en cuarentena.

### Listar las respuestas de un formulario

Paginadas, de la más reciente a la más antigua. Las respuestas exponen los valores por id de campo y nunca incluyen datos de contacto del cliente, solo `clientId`. Cada respuesta incluye `responseChannel`: el canal por el que llegó (`whatsapp`, `email`, `review-app`, `widget-web`, `mobile-sdk`, `api`, o un valor personalizado definido por ti); es `null` en respuestas históricas.

**Endpoint**

```
GET /v1/forms/:id/responses
```

**Respuestas**

* `200 OK`: Lista paginada de respuestas.
* `403 Forbidden`: El formulario pertenece a otra empresa.
* `404 Not Found`: No existe el formulario.

## Flows

Endpoints de solo lectura. Un flow encadena varios wokus (y opcionalmente un NPS) en un solo recorrido; con estos datos puedes renderizar el recorrido en tu aplicación y capturar con los endpoints existentes (`POST /v1/wokus/:id/textnotes`, `POST /v1/nps`, etc.).

* `GET /v1/flows`: Lista paginada de los flows de tu empresa.
* `GET /v1/flows/:id`: Datos del flow: wokus en orden, branding y NPS vinculado.

**Respuestas**

* `200 OK`: Datos del flow o lista paginada.
* `403 Forbidden`: El flow pertenece a otra empresa.
* `404 Not Found`: No existe el flow.

## Invitaciones

Familia homogénea de endpoints para enviar encuestas por **correo** o **WhatsApp** a una lista de hasta 100 destinatarios. Los envíos son asíncronos: la API responde de inmediato con el detalle por destinatario.

**Endpoints**

```
POST /v1/nps/invitations
POST /v1/csat/invitations
POST /v1/ces/invitations
POST /v1/forms/:id/invitations
POST /v1/wokus/:id/invitations
```

**Body**

```json theme={null}
{
  "channel": "whatsapp",
  "recipients": ["56912345678"],
  "language": "es"
}
```

**Campos**

* `channel` *(string, obligatorio)*: `email` o `whatsapp`.
* `recipients` *(string\[], obligatorio, máx 100)*: Correos para el canal `email`; números con código de país (por ejemplo `56912345678`) para `whatsapp`.
* `language` *(string, opcional)*: `es` o `en`.
* `npsToolId` *(string, opcional, solo NPS)*: Si se omite, se envía la encuesta NPS a nivel de compañía.
* `csatToolId` / `cesToolId` *(string, obligatorio en CSAT y CES)*.

**Respuestas**

* `202 Accepted`: Resumen por destinatario: `accepted` (despachados o encolados) y `rejected` con el motivo (`invalid_recipient`, `quarantined`, `insufficient_credits_or_blocked`, `send_failed`).
* `400 Bad Request`: Validación fallida, o el formulario o woku está cerrado.
* `403 Forbidden`: La herramienta no pertenece a tu empresa.
* `404 Not Found`: No existe el formulario o el woku.

<Tip>
  Los destinatarios mal formados no hacen fallar la solicitud completa: se reportan individualmente en `rejected` con el motivo `invalid_recipient`.
</Tip>

## Cuarentenas

Consulta de solo lectura para saber si un respondente está actualmente en cuarentena, antes de pedirle feedback. No registra nada. Las reglas anti-duplicado se configuran en el panel administrativo; ver [Cuarentenas](/docs/configuration/quarantines).

**Endpoint**

```
GET /v1/quarantines/check?email=<email>
```

**Query params**

* `email` *(string, opcional)*.
* `phone` *(string, opcional)*.

Se requiere al menos uno de los dos.

**Respuestas**

* `200 OK`: Estado de cuarentena del respondente.
* `400 Bad Request`: No se entregó ni `email` ni `phone`.
* `403 Forbidden`: Clave inválida.

## Reportes NPS

Si tu empresa usa la herramienta **NPS**, puedes consultar los reportes agregados directamente desde la API para integrarlos en tus propios dashboards.

### Reporte NPS de la compañía

Devuelve el reporte agregado de **todos los NPS** registrados para tu empresa.

**Endpoint**

```
GET /v1/reports/company-nps
```

**Headers**

* `Authorization: Bearer <Company-Key>`

**Respuestas**

* `200 OK`: Reporte con distribución de promotores, pasivos y detractores, y el puntaje NPS resultante.
* `403 Forbidden`: Clave inválida.

### Reporte NPS de una herramienta

Devuelve el reporte de un **NPS Tool** específico (por ejemplo, "NPS post-evento" o "NPS post-compra").

**Endpoint**

```
GET /v1/reports/nps-tool/:id
```

**Headers**

* `Authorization: Bearer <Company-Key>`

**Path params**

* `id` *(string, obligatorio)*: Identificador del NPS Tool.

**Respuestas**

* `200 OK`: Reporte del NPS Tool.
* `400 Bad Request`: `id` mal formado.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: El NPS Tool no existe o no pertenece a tu empresa.

## Trackers Externos

Los **trackers externos** te permiten asociar identificadores de tus sistemas (CRM, ERP, ticketing, OMS) a tus herramientas de feedback (wokus, NPS, CSAT, CES, formularios y flows) para luego buscarlas cruzadas. Por ejemplo: vincular un woku al `transaction_id` de tu CRM o al `order_id` de tu ERP.

### Modelo

* **Tracker definition**: Una entrada de catálogo a nivel de empresa, como `{ name: "trr", system: "crm interno", description: "..." }`. Se define en el panel administrativo de woku, en la sección Empresa.
* **Tracker value**: Un valor string ligado a un par `(herramienta, Tracker)`. Varias herramientas pueden compartir el mismo valor.

### Listar trackers activos de la empresa

**Endpoint**

```
GET /v1/external-trackers
```

**Query params**

* `page` *(number, opcional)*.
* `limit` *(number, opcional)*.

**Respuestas**

* `200 OK`: Lista paginada de definiciones de trackers.
* `403 Forbidden`: Clave inválida.

### Listar trackers asignados a un woku

**Endpoint**

```
GET /v1/external-trackers/wokus/:id
```

**Respuestas**

* `200 OK`: Trackers asignados al woku.
* `403 Forbidden`: Clave inválida.

### Asignar un valor de tracker a un woku

Crea o actualiza (upsert) el valor de un tracker en un woku.

**Endpoint**

```
POST /v1/external-trackers/wokus/:id
```

**Body**

```json theme={null}
{
  "name": "trr",
  "value": "TX-2026-00043"
}
```

**Campos**

* `name` *(string, obligatorio, máx 60)*: Nombre del tracker definido en tu empresa.
* `value` *(string, obligatorio, máx 500)*: Valor del identificador externo (siempre se almacena como string).

**Respuestas**

* `201 Created`: Valor asignado o actualizado.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: El woku o el tracker no existen.

### Eliminar un valor de tracker de un woku

**Endpoint**

```
DELETE /v1/external-trackers/wokus/:id/:trackerName
```

**Respuestas**

* `200 OK`: Eliminado.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: El woku o el tracker no existen.

### Buscar wokus por tracker

Encuentra todos los wokus que tienen un determinado `(name, value)`. Útil para volver desde tu CRM al woku correspondiente.

**Endpoint**

```
GET /v1/external-trackers/search?name=<tracker>&value=<value>
```

**Query params**

* `name` *(string, obligatorio)*: Nombre del tracker.
* `value` *(string, obligatorio)*: Valor a buscar.
* `page` *(number, opcional)*.
* `limit` *(number, opcional)*.

**Respuestas**

* `200 OK`: Lista paginada de wokus con sus tracker values.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existen coincidencias.

### Trackers en herramientas VoC (NPS, CSAT, CES, formularios, flows)

Además de los wokus, puedes asignar trackers a las demás herramientas de feedback. El tipo de herramienta va en la ruta como `:entityType`, con uno de estos valores: `nps`, `csat`, `ces`, `form`, `flow`. El `:id` es el id de la herramienta (por ejemplo `npsToolId`, `csatToolId`, `cesToolId`, `formId` o `flowId`).

En la versión 2 de la API estos endpoints y los de wokus se unificarán en un único recurso polimórfico.

#### Listar trackers asignados a una herramienta

**Endpoint**

```
GET /v1/external-trackers/:entityType/:id
```

Ejemplo: `GET /v1/external-trackers/nps/671000000000000000000001`.

**Respuestas**

* `200 OK`: Trackers asignados a la herramienta.
* `400 Bad Request`: Tipo de herramienta no soportado.
* `403 Forbidden`: Clave inválida.

#### Asignar un valor de tracker a una herramienta

Crea o actualiza (upsert) el valor de un tracker en la herramienta.

**Endpoint**

```
POST /v1/external-trackers/:entityType/:id
```

**Body**

```json theme={null}
{
  "name": "trr",
  "value": "TX-2026-00043"
}
```

**Campos**

* `name` *(string, obligatorio, máx 60)*: Nombre del tracker definido en tu empresa.
* `value` *(string, obligatorio, máx 500)*: Valor del identificador externo (siempre se almacena como string).

**Respuestas**

* `201 Created`: Valor asignado o actualizado.
* `400 Bad Request`: Tipo no soportado, tracker inactivo o validación fallida.
* `403 Forbidden`: La herramienta pertenece a otra empresa.
* `404 Not Found`: La herramienta o el tracker no existen.

#### Eliminar un valor de tracker de una herramienta

**Endpoint**

```
DELETE /v1/external-trackers/:entityType/:id/:trackerName
```

**Respuestas**

* `200 OK`: Eliminado.
* `404 Not Found`: El tracker o la asignación no existen.

## Destinos de tickets

Los **destinos de tickets** definen a dónde llegan los tickets de soporte que genera la IA de woku: una plataforma SAC (Zendesk, Salesforce), un canal de Slack, un servicio HTTP propio o una lista de correos. Cada destino tiene reglas de enrutamiento basadas en tus trackers externos que deciden qué tickets le llegan.

### Modelo

* `kind` *(obligatorio)*: `zendesk`, `salesforce`, `slack`, `custom` o `email`.
* `config` *(obligatorio)*: configuración no sensible, específica de cada `kind`.
  * `zendesk`: `{ connectionId, groupId? }`. `connectionId` es el id de tu Integración de Zendesk conectada en el panel administrativo; `groupId` es el grupo de agentes, opcional.
  * `salesforce`: `{ queueId?, caseOrigin? }`. `queueId` es el Id de una Queue de Salesforce (`"00G..."`); `caseOrigin` es `"Web"` por defecto.
  * `slack`: `{ connectionId, channelId, channelLabel? }`. `connectionId` es el id de tu Integración de Slack; `channelId` es el id del canal (`"C..."`).
  * `custom`: `{ url, method, headers? }`. `method` es `POST` o `PUT`; `headers` son cabeceras extra no sensibles.
  * `email`: `{ emails }`. Hasta 20 destinatarios.
* `credentials` *(obligatorio, de solo escritura)*: nunca se devuelve en las respuestas, solo su `credentialsHint` (últimos 4 caracteres enmascarados). Zendesk, Salesforce y Slack se autentican con la Integración conectada de tu empresa, así que envías `{}`. Custom usa `{ authType, ... }`, con `authType` `none`, `basic` (agrega `username`, `password`), `bearer` (agrega `token`) o `api-key-header` (agrega `headerName`, `headerValue`). Email tampoco usa credenciales, envías `{}`.
* `routingConditions` *(opcional, hasta 20 filas)*: condiciones sobre tus trackers externos. Cada fila usa el operador `equals` (el tracker debe tener un valor exacto en `value`) o `any` (cualquier valor del tracker, se omite `value`). `relationToPrevious` une una fila con la anterior (`AND` mismo grupo, `OR` grupo nuevo); la primera fila no lo incluye. El destino recibe el ticket cuando algún grupo OR se cumple completo.

### Listar los destinos de tickets

**Endpoint**

```
GET /v1/ticket-destinations
```

**Respuestas**

* `200 OK`: Lista de destinos (sin credenciales).
* `403 Forbidden`: Clave inválida.

### Obtener un destino de tickets

**Endpoint**

```
GET /v1/ticket-destinations/:id
```

**Respuestas**

* `200 OK`: Destino.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el destino.

### Crear un destino de tickets

**Endpoint**

```
POST /v1/ticket-destinations
```

**Headers**

* `Content-Type: application/json`
* `Authorization: Bearer <Company-Key>`

**Body (ejemplo con Slack, enrutado por cualquier valor de un tracker)**

```json theme={null}
{
  "name": "Slack #soporte",
  "kind": "slack",
  "config": {
    "connectionId": "671000000000000000000010",
    "channelId": "C0123ABCD",
    "channelLabel": "#soporte"
  },
  "credentials": {},
  "routingConditions": [
    { "trackerId": "671000000000000000000011", "operator": "any" }
  ]
}
```

**Campos**

* `name` *(string, obligatorio, máx 120)*.
* `kind`, `config`, `credentials`, `routingConditions`: ver la sección Modelo de este apartado.
* `aiContext` *(string, opcional, máx 2000)*: contexto adicional para el triage de IA de este destino.
* `template` *(object, opcional, solo `kind: "custom"`)*: `{ preset, body? }`. Sin `body`, se envía el sobre canónico del ticket.

**Respuestas**

* `201 Created`: Destino creado.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: Clave inválida.

### Actualizar un destino de tickets

**Endpoint**

```
PATCH /v1/ticket-destinations/:id
```

Todos los campos son opcionales; solo se cambian los que envías. Enviar `credentials` rota el secreto guardado. Enviar `kind` cambia el proveedor: `config` y `credentials` se validan contra el nuevo `kind`.

**Body (ejemplo: deshabilitar)**

```json theme={null}
{ "enabled": false }
```

**Respuestas**

* `200 OK`: Destino actualizado.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el destino.

### Eliminar un destino de tickets

**Endpoint**

```
DELETE /v1/ticket-destinations/:id
```

Sus condiciones de enrutamiento viven en el destino y desaparecen con él.

**Respuestas**

* `200 OK`: `{ "deleted": true }`.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el destino.

## Grupos de planes de acción

Un **grupo de plan de acción** define qué feedback (por condiciones de trackers) genera un plan de mejora y quiénes son los responsables. Cuando el contador de comentarios nuevos de tipo mejora alcanza el `threshold` del grupo, woku redacta un plan con IA para sus miembros.

### Modelo

* `conditions` *(obligatorio, mínimo 1 fila, hasta 20)*: mismas reglas que `routingConditions` de los destinos de tickets (operador `equals` o `any`, `relationToPrevious` para armar AND/OR).
* `members` *(obligatorio, mínimo 1)*: cada uno con `userId` y `role` (`admin` aprueba y envía planes, `assignee` trabaja las tareas). Un grupo necesita al menos un `admin` y un `assignee`.
* `threshold` *(integer, opcional, mínimo 1, por defecto 300)*: comentarios nuevos de tipo mejora que disparan un borrador.
* `enabled`: un grupo deshabilitado deja de contar feedback y de generar borradores, pero conserva su historial y estadísticas.

### Listar los grupos de planes de acción

**Endpoint**

```
GET /v1/action-plan-groups
```

**Query params**

* `search` *(string, opcional)*: búsqueda libre por nombre, descripción y valores de condición.

**Respuestas**

* `200 OK`: Lista de grupos.
* `403 Forbidden`: Clave inválida.

### Obtener un grupo de planes de acción

**Endpoint**

```
GET /v1/action-plan-groups/:id
```

**Respuestas**

* `200 OK`: Grupo.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el grupo.

### Crear un grupo de planes de acción

**Endpoint**

```
POST /v1/action-plan-groups
```

**Headers**

* `Content-Type: application/json`
* `Authorization: Bearer <Company-Key>`

**Body**

```json theme={null}
{
  "name": "Atención en tienda",
  "description": "Reclamos sobre la experiencia en tienda física.",
  "conditions": [
    { "trackerId": "671000000000000000000011", "operator": "any" }
  ],
  "members": [
    { "userId": "671000000000000000000020", "role": "admin" },
    { "userId": "671000000000000000000021", "role": "assignee" }
  ],
  "threshold": 300
}
```

**Campos**: ver la sección Modelo de este apartado, más `name` *(string, obligatorio, máx 120)* y `description` *(string, opcional, máx 500)*.

**Respuestas**

* `201 Created`: Grupo creado.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: Clave inválida.

### Actualizar un grupo de planes de acción

**Endpoint**

```
PATCH /v1/action-plan-groups/:id
```

Todos los campos son opcionales; enviar `description: ""` la limpia.

**Respuestas**

* `200 OK`: Grupo actualizado.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el grupo.

### Habilitar o deshabilitar un grupo

**Endpoint**

```
PATCH /v1/action-plan-groups/:id/enabled
```

**Body**

```json theme={null}
{ "enabled": false }
```

**Respuestas**

* `200 OK`: Grupo actualizado.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el grupo.

### Eliminar un grupo de planes de acción

**Endpoint**

```
DELETE /v1/action-plan-groups/:id
```

Los planes ya generados conservan su propio detalle congelado y solo referencian al grupo por id.

**Respuestas**

* `200 OK`: `{ "deleted": true }`.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el grupo.

## Planes de acción

Los **planes de acción** son planes de mejora redactados por IA a partir del feedback de un grupo. Un plan nace en estado `draft`, se aprueba, y desde ahí se envía a una herramienta externa (Jira, monday.com, ClickUp o Notion) o se trabaja dentro de woku con el proveedor `internal` ("Gestionar en woku", un kanban interno).

### Modelo

* `status`: `draft`, `approved`, `sent`, `canceled`, `delivery_error`, `in_progress` (gestionado dentro de woku) o `completed`.
* `priority`: `high`, `medium` o `low`, calculada por woku (nunca la define la IA).
* `evidence`: el recorte de feedback congelado al momento de generar el plan; sus números nunca se recalculan.
* `tasks`: lista de tareas del plan. En un plan gestionado (`in_progress`) cada tarea además tiene `status` (`todo`, `in_progress`, `done`) y `assigneeId`.

### Listar los planes de acción

**Endpoint**

```
GET /v1/action-plans
```

**Query params**

* `groupId`, `status`, `source` (`woku`, `nps`, `csat` o `ces`), `priority`, `from`, `to`, `search`, `page`, `limit`: todos opcionales.

**Respuestas**

* `200 OK`: `{ "data": [...], "total": 42 }`. A diferencia de otros listados de esta API, esta respuesta no repite `page` ni `limit`.
* `403 Forbidden`: Clave inválida.

### Obtener un plan de acción

**Endpoint**

```
GET /v1/action-plans/:id
```

Devuelve el plan completo, incluyendo el detalle de evidencia congelada y el estado de envío.

**Respuestas**

* `200 OK`: Plan.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el plan.

### Enviar un plan a un destino

**Endpoint**

```
POST /v1/action-plans/:id/send
```

El plan debe estar `approved` (o `delivery_error` para reintentar) para enviarlo a un proveedor externo. `provider: "internal"` es la excepción: gestiona el plan dentro de woku, no lleva `target` y aplica de inmediato (el plan pasa a `in_progress`). Los proveedores externos encolan el envío (`202 Accepted`); consulta `GET /v1/action-plans/:id` para ver el resultado.

**Body (gestionar dentro de woku)**

```json theme={null}
{ "provider": "internal" }
```

**Body (enviar a Jira)**

```json theme={null}
{
  "provider": "jira",
  "target": { "projectId": "10032", "issueTypeId": "10001" },
  "resourceLabel": "Operaciones CX - Backlog"
}
```

**Campos**

* `provider` *(string, obligatorio)*: `jira`, `monday`, `clickup`, `notion` o `internal`.
* `target` *(object, obligatorio salvo `internal`)*: `jira` es `{ siteId?, projectId, issueTypeId }`; `monday` es `{ boardId, groupId }`; `clickup` es `{ listId }`; `notion` es `{ databaseId }`.
* `resourceLabel` *(string, opcional, máx 300)*: descripción legible del destino.

**Respuestas**

* `202 Accepted`: `{ "status": "sending" }` para un proveedor externo, `{ "status": "managed" }` para `internal`.
* `400 Bad Request`: Falta o es inválido el `target` del proveedor.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el plan.
* `409 Conflict`: El plan no está en un estado válido para esta transición, o ya hay un envío en curso.

### Tareas de un plan

Disponibles mientras el plan está en `draft` o `in_progress` (gestionado). El `status` y el `assigneeId` de una tarea solo se editan en un plan gestionado.

**Endpoints**

```
POST /v1/action-plans/:id/tasks
PATCH /v1/action-plans/:id/tasks/reorder
PATCH /v1/action-plans/:id/tasks/:taskId
DELETE /v1/action-plans/:id/tasks/:taskId
```

* **Agregar** (`POST .../tasks`): body `{ "text": "..." }`, máximo 160 caracteres, hasta 100 tareas por plan.
* **Reordenar** (`PATCH .../tasks/reorder`): body `{ "orderedTaskIds": [...] }` con TODOS los ids de tareas del plan, en el orden deseado.
* **Editar** (`PATCH .../tasks/:taskId`): body con al menos uno de `text`, `status` o `assigneeId`.
* **Eliminar** (`DELETE .../tasks/:taskId`): sin body.

Las cuatro devuelven el plan completo actualizado.

**Respuestas**

* `200 OK` (`201 Created` al agregar): Plan actualizado.
* `400 Bad Request`: Validación fallida.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el plan o la tarea.
* `409 Conflict`: El plan no está en `draft` ni `in_progress`, o se envió `status`/`assigneeId` en un plan no gestionado.

### Cambiar el estado de un plan

**Endpoints**

```
POST /v1/action-plans/:id/approve
POST /v1/action-plans/:id/reopen
POST /v1/action-plans/:id/cancel
POST /v1/action-plans/:id/complete
POST /v1/action-plans/:id/resume
```

Sin body. Cada acción exige que el plan esté en un estado de origen específico:

| Acción     | Desde                                                | Hacia         |
| ---------- | ---------------------------------------------------- | ------------- |
| `approve`  | `draft`                                              | `approved`    |
| `reopen`   | `approved`, `delivery_error`                         | `draft`       |
| `cancel`   | `draft`, `approved`, `delivery_error`, `in_progress` | `canceled`    |
| `complete` | `in_progress`                                        | `completed`   |
| `resume`   | `completed`                                          | `in_progress` |

**Respuestas**

* `200 OK`: Plan actualizado.
* `403 Forbidden`: Clave inválida.
* `404 Not Found`: No existe el plan.
* `409 Conflict`: El plan no está en el estado de origen requerido para esa acción.

## Límites de ingesta y extracción

La API REST está documentada de forma interactiva en la pestaña **API
Reference** y aplica **límites de tasa y de volumen** tanto para la
**ingesta** (creación de wokus, reseñas, NPS, asignación de trackers) como
para la **extracción** (consulta de wokus, reportes y búsquedas). Los límites
cubren los dos modos de operación: **por evento** (una llamada por registro) y
**por lote** (cargas batch).

Los límites se aplican por **Clave de Compañía**:

| Operación                 | Modo          | Límite                                           |
| ------------------------- | ------------- | ------------------------------------------------ |
| Ingesta por evento        | Evento        | 600 solicitudes/minuto · hasta 50 eventos/seg    |
| Ingesta por lote          | Lote          | Hasta 5.000 registros por lote · 60 lotes/minuto |
| Volumen diario de ingesta | Evento + lote | 1.000.000 registros/día                          |
| Extracción (lectura)      | Evento        | 300 solicitudes/minuto                           |
| Extracción paginada       | Lote          | Hasta 200 registros por página                   |

<Note>
  Estos son los límites por defecto del plan **Corporate**. Si tu integración
  necesita ventanas mayores (picos estacionales, migraciones masivas),
  escríbenos para ampliarlos.
</Note>

### Manejo de 429 (Too Many Requests)

Cuando superas un límite de tasa, la API responde con `429 Too Many Requests`
e incluye encabezados que te indican cuándo reintentar:

* `Retry-After`: segundos a esperar antes del siguiente intento.
* `X-RateLimit-Limit`: tope de la ventana actual.
* `X-RateLimit-Remaining`: solicitudes restantes en la ventana.
* `X-RateLimit-Reset`: marca de tiempo en que se reinicia la ventana.

Implementa **reintentos con backoff exponencial** respetando `Retry-After`.
Para cargas batch, divide los lotes que excedan el máximo de registros en
lugar de reintentar el lote completo.

### Paginación en la extracción

Los endpoints de extracción que devuelven colecciones son **paginados**.
Controla la página con los query params `page` y `limit` (por ejemplo, en
`GET /v1/external-trackers` o `GET /v1/external-trackers/search`). Para
extraer grandes volúmenes, recorre las páginas de forma secuencial respetando
el límite de extracción por minuto y el máximo de registros por página.

```
GET /v1/external-trackers/search?name=trr&value=TX-2026-00043&page=1&limit=200
```

## Errores comunes

### URL de archivo inaccesible

Si usas `POST /v1/wokus` con `fileUrl`, asegúrate de que la URL sea **públicamente accesible**. Nuestro servicio descarga el archivo antes de crear el woku.

### Clave secundaria duplicada

`folderSecondaryKey` se usa para identificar una carpeta dentro de tu empresa. **No pueden existir dos carpetas con la misma clave** en una misma empresa. Si necesitas reutilizar una clave, primero elimina la carpeta existente desde el panel administrativo.

### 403 en todas las llamadas

Casi siempre significa que la clave es incorrecta, está caducada o pertenece a otra empresa. Vuelve a obtener la clave desde [admin.woku.app](https://admin.woku.app) → Información de la empresa → **Obtener Clave**.

## Contacto y soporte

* Diego Orrego Brito, CTO de woku.
* Correo: [diego@woku.app](mailto:diego@woku.app) (incluye el nombre de la empresa en el asunto y menciona que se trata de la API).
