npm package: @getsupervisor/speech-analytics-sdk
Description: SDK JS/TS oficial para interactuar con la Public API de Speech Analytics.
Latest version: 0.0.1-patch.1

# @getsupervisor/speech-analytics-sdk

SDK JS/TS oficial para interactuar con la Public API de Speech Analytics.

## Convenciones para releases del SDK

- Usa commits convencionales con el scope `sdk-js` para **cualquier cambio dentro de `apps/public-api/sdk/js/**`** (ejemplo: `feat(sdk-js): agrega soporte a X`).
- Solo los commits con ese scope generan nuevas versiones y entradas en el CHANGELOG; otros cambios quedan ignorados por el pipeline de publicación.
- Si un PR mezcla cambios del SDK con otros módulos, asegúrate de dividirlos o añadir un commit independiente con el scope correcto para que la release refleje únicamente los cambios del cliente.

Sin el scope `sdk-js` el workflow de publicación puede ejecutarse, pero no cortará una release en npm.

## Instalación

```bash
npm i @getsupervisor/speech-analytics-sdk
```

## Ejemplos

En [apps/public-api/sdk/js/examples](apps/public-api/sdk/js/examples) hay ejemplos ejecutables con `tsx`.

```bash
# base URL del API (por defecto localhost)
export API_BASE_URL='http://localhost:3000/api/v1'

# si tu API requiere Bearer token
export API_TOKEN='...'

# requerido para endpoints que usan x-workspace-id
export WORKSPACE_ID='...'

yarn examples:auth
yarn examples:api-keys
```

## Uso básico

```ts
import { createClient } from '@getsupervisor/speech-analytics-sdk';

const client = createClient({
  baseUrl: 'https://app.getsupervisor.ai',
  headers: { Authorization: 'Bearer <token>' },
  workspaceId: 'ws-123',
});

const { data: agents } = await client.agents.list({ limit: 10 });
const agent = await client.agents.get('agent-123');

const { data: versions } = await client.agents
  .versions(agent.agentId)
  .list({ limit: 1 });
const versionId = versions[0]?.id;
if (!versionId) {
  throw new Error('Agent has no versions yet');
}

// helpers de agente sin repetir IDs
const instructions = client.agents
  .versions(agent.agentId)
  .instructions(versionId);
await instructions.create({
  order: 1,
  content: 'Responde en español neutro',
  status: 'inactive',
});

// administrar versiones e instrucciones
await client.agents
  .versions(agent.agentId)
  .instructions('version-1')
  .create({ order: 1, content: 'Saluda con empatía', status: 'active' });

// administrar blueprints por versión
const blueprints = client.agents.blueprints(agent.agentId);
await blueprints.version('version-1').create({
  languageId: 'es-MX',
  personalityName: 'Embajador de marca',
  personalityRole: 'Consultor de adopción',
  targetAudience: 'Usuarios nuevos',
  mainGoal: 'Incrementar la activación',
  personalityInitialGreeting: '¡Hola! Soy tu asistente de onboarding.',
  personalityMessageStyleId: 'style-uuid',
  personalityToneStyleId: 'tone-uuid',
  requiredData: ['industry'],
  criticalRules: ['Nunca compartas precios internos'],
  topicsAllowed: ['onboarding', 'soporte'],
  topicsForbidden: ['facturación'],
});

// los helpers del entity también exponen blueprints
const agentBlueprint = await agent.blueprints.version('version-1').get();
console.log(agentBlueprint.personalityName);

// catálogo y recursos de tools
const tools = await client.tools.list({ agentType: 'chat' });
await client.tools.uploadResource(tools.data[0].identifier, formData);

// eliminar un agente
await client.agents.delete(agent.agentId);

// listar workspaces y tomar el primero (refrescando caché local)
const workspaces = await client.workspaces.list({
  limit: 10,
  refreshCache: true,
});
const firstWorkspaceId = workspaces.data[0]?.id;
if (firstWorkspaceId) {
  client.workspace.set(firstWorkspaceId);
}

// campañas masivas desde CSV
const campaign = await client.campaigns.create(agent.agentId, versionId, {
  name: 'Holiday follow-up',
  objective: 'Re-engage dormant customers',
  file: new File([csvBlob], 'campaign-rows.csv', { type: 'text/csv' }),
});

const recentCampaigns = await client.campaigns.list({ limit: 10 });
console.log(recentCampaigns.data.map((item) => item.name));

// ejecuciones individuales con correlación al toolExecutionId
const executions = await client.campaigns.listExecutions(campaign.campaignId, {
  limit: 20,
});

executions.data.forEach((row) => {
  console.log(
    `Row ${row.rowNumber} => status=${row.status} agentExecutionId=${row.agentExecutionId}`,
  );
});
// agentExecutionId coincide con el toolExecutionId expuesto por client.tools.execute
```

## Catálogos (Catalogs)

Los catálogos exponen valores normalizados reutilizables (idiomas, estilos de mensaje, estilos de tono, etiquetas y voces).

Namespace: `client.catalogs`

Operaciones disponibles:

- `catalogs.list(options?)` — lista items paginados con filtros y búsqueda.
- `catalogs.get(itemId)` — obtiene el detalle de un item.
- `catalogs.create(payload)` — crea un nuevo item de catálogo.
- `catalogs.remove(itemId)` — elimina (soft-delete) un item.

Ejemplos:

```ts
// listar idiomas globales con búsqueda y paginación
const page1 = await client.catalogs.list({
  search: 'spanish',
  filter: { type: 'language', scope: 'global' },
  limit: 20,
});

const page2 = await page1.next();

// crear un idioma global
const language = await client.catalogs.create({
  type: 'language',
  scope: 'global',
  name: 'Español (México)',
  description: 'Variante de español latino con modismos mexicanos',
  metadata: { code: 'es', locale: 'es-MX' },
});

// crear una voz por workspace (requiere workspaceId)
const voice = await client.catalogs.create({
  type: 'voice',
  scope: 'workspace',
  workspaceId: client.workspace.get(),
  name: 'Brand Voice A',
  description: 'Voz femenina, cálida',
  metadata: {
    language: 'es',
    gender: 'female',
    tone: 'warm',
    provider: 'elevenlabs',
    previewUrl: 'https://cdn.example.com/voices/brand-a.mp3',
  },
});

// eliminar por id
await client.catalogs.remove(language.data.id);
```

Notas:

- Campos `type` válidos: `language`, `message_style`, `tone_style`, `tag`, `voice`.
- `scope` puede ser `global` o `workspace`. Si es `workspace`, envía `workspaceId`.
- El shape de `metadata` depende de `type` (ver ejemplos arriba).

## Versiones de agente (Agent Versions)

Namespace: `client.agentVersions`

Operaciones clave:

- `agentVersions.list(agentId, { page, limit, filter })`
- `agentVersions.get(agentId, versionId)`
- `agentVersions.create(agentId, payload?)` — crea versión en `draft`.
- `agentVersions.clone(agentId, versionId)` — clona una versión.
- `agentVersions.publish(agentId, versionId, payload?)` — publica y marca como `active`.
- `agentVersions.updateNotes(agentId, versionId, { notes })`
- Instrucciones por versión:
  - `agentVersions.listInstructions(agentId, versionId, opts?)`
  - `agentVersions.createInstruction(agentId, versionId, dto)`
  - `agentVersions.updateInstruction(agentId, versionId, instructionId, dto)`
  - `agentVersions.deleteInstruction(agentId, versionId, instructionId)`

Ejemplo:

```ts
// crear una nueva versión draft
const v1 = await client.agentVersions.create(agent.agentId);

// agregar instrucciones a la versión
await client.agentVersions.createInstruction(agent.agentId, v1.id, {
  order: 0,
  content: 'Saluda con empatía y brevedad',
});

// publicar la versión
const active = await client.agentVersions.publish(agent.agentId, v1.id, {
  notes: 'Primera publicación estable',
});

// clonar una versión activa (crea un nuevo draft)
const v2 = await client.agentVersions.clone(agent.agentId, active.id);
```

Estados soportados: `draft`, `active`, `archived`.

## Stages del blueprint

Modela el flujo conversacional de cada agente con los helpers `client.agents.stages(agentId)`, indicando siempre qué blueprint deseas administrar.

```ts
const stages = client.agents.stages(agent.agentId);
const activeBlueprint = await client.agents.blueprints(agent.agentId).list();
const blueprintId = activeBlueprint.data[0].blueprint.blueprintId;

// Paginar stages existentes
const page = await stages.list(blueprintId, { limit: 10, search: 'greeting' });
console.log(page.meta.total);

// También puedes obtener un helper con el blueprint preseleccionado
const blueprintStages = stages(blueprintId);

// Crear un stage
const created = await blueprintStages.create({
  name: 'proposal',
  title: 'Propuesta y cierre',
  goalPrompt:
    'Explica la solución recomendada y ofrece una llamada con humano.',
  order: 2,
  metadata: { requiresSupervisor: false },
});

// Actualizar y reordenar
await blueprintStages.update(created.id, {
  goalPrompt: 'Resume beneficios en < 3 bullets y ofrece agendar demo.',
});

await stages.reorder(blueprintId, {
  stageIds: [page.data[0].id, created.id],
  startingStageName: 'greeting',
});

// Triggers por stage
const triggers = stages.triggers(blueprintId, created.id);
await triggers.create({
  condition: { type: 'intent', value: 'schedule_call' },
  nextStageName: 'handoff',
});

const listing = await triggers.list();
console.log(listing.data[0].condition);
```

Los métodos de triggers permiten `list`, `get`, `create`, `update` y `delete`. Recuerda habilitar los scopes `blueprint-stages:*` y `stage-triggers:*` en la credencial utilizada.

## Horarios del agente (Agent Schedules)

Namespaces: `client.agentSchedule` y `client.agentScheduleExceptions`

Operaciones de horario semanal (`agentSchedule`):

- `get(agentId)` — obtiene el horario semanal.
- `create(agentId, dto)` — crea/establece horario.
- `update(agentId, dto)` — actualiza horario.

Ejemplo de horario:

```ts
// activar horario de lunes a viernes con timezone America/Mexico_City
await client.agentSchedule.create(agent.agentId, {
  timezone: 'America/Mexico_City',
  isEnabled: true,
  monday: { enabled: true, start: '09:00', end: '18:00' },
  tuesday: { enabled: true, start: '09:00', end: '18:00' },
  wednesday: { enabled: true, start: '09:00', end: '18:00' },
  thursday: { enabled: true, start: '09:00', end: '18:00' },
  friday: { enabled: true, start: '09:00', end: '18:00' },
  saturday: { enabled: false },
  sunday: { enabled: false },
  outOfHoursBehavior: 'offline',
  outOfHoursMessage: 'Nuestro equipo está fuera de horario. Vuelve más tarde.',
});
```

Operaciones de excepciones por fecha (`agentScheduleExceptions`):

- `list(agentId, opts?)` — paginado con filtros (por rango de fechas, etc.).
- `get(agentId, exceptionId)`
- `create(agentId, dto)` — soporte para `isClosed` o ventana `startTime`/`endTime`.
- `update(agentId, exceptionId, dto)`
- `delete(agentId, exceptionId)`

Ejemplo de excepciones:

```ts
// feriado: cerrado todo el día
await client.agentScheduleExceptions.create(agent.agentId, {
  exceptionDate: '2025-11-20',
  isClosed: true,
  reason: 'Día de la Revolución',
});

// media jornada en una fecha específica
await client.agentScheduleExceptions.create(agent.agentId, {
  exceptionDate: '2025-12-24',
  isClosed: false,
  startTime: '09:00',
  endTime: '13:00',
  reason: 'Nochebuena',
});
```

### Ejemplo de CRUD completo

Consulta el archivo `examples/agents-crud.ts` para ver un flujo end-to-end que:

- crea un agente temporal,
- lista agentes con paginación,
- actualiza los campos con `save` y `update`, y
- elimina el recurso al finalizar.

Ejecuta el ejemplo definiendo `API_BASE_URL`, `WORKSPACE_ID` y `API_TOKEN`:

```bash
API_BASE_URL=https://app.getsupervisor.ai \
WORKSPACE_ID=ws-123 \
API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... \
npx tsx examples/agents-crud.ts
```

## Tools y conexiones

Namespace: `client.tools`

- `tools.list(options?)` — catálogo paginado de tools con filtros por tipo de agente.
- `tools.listResources(toolId, options?)` — recursos cargados (docs, media, archivos).
- `tools.uploadResource(toolId, payload)` — carga o actualiza recursos usando `FormData`.
- `tools.execute(toolId, payload, { idempotencyKey? })` — ejecuta una acción declarada en la tool.
- `tools.connect(toolId, payload, { idempotencyKey? })` — crea una conexión entre un agente y la tool.

Ejemplo de conexión a `voice.calls` y lectura de las guías de uso:

```ts
const connection = await client.tools.connect('voice.calls', {
  agentId: agent.agentId,
  workspaceId: client.workspace.get(),
  metadata: {
    // Configuración adicional que quieras adjuntar a la conexión
  },
});

console.log(connection.descriptionUsage);
// Utiliza la herramienta voice.calls para iniciar una llamada saliente...

console.log(connection.usageExample);
/*
El usuario pide llamar al número +52-55-0000-0000 para confirmar una cita.
1. Ejecuta voice.calls con action "startCall" enviando { to, metadata: { reason: 'confirmar cita' } }.
2. Espera el resultado de la llamada y comparte un resumen breve con el usuario.
*/
```

Cuando la tool es `voice.calls`, el servicio de backend rellena `descriptionUsage` y
`usageExample` con una guía estática descrita en `apps/public-api/src/tools/application/templates/voice-call-usage.template.ts`.
Consulta `docs/mcp/tools_execution.md#9-guías-de-uso-estándar` si necesitas replicar esta guía en otros canales (por ejemplo, recursos MCP).
Puedes usar estos campos para mostrar recomendaciones al usuario final o complementar el prompt del agente.

### Ejemplo: conectar un webhook HTTP personalizado

El script `examples/custom-tool-connection.ts` muestra cómo registrar una tool que invoca un webhook externo mediante API Key. Define las variables necesarias y ejecuta el ejemplo:

```bash
export API_BASE_URL="https://app.getsupervisor.ai"
export WORKSPACE_ID="ws-123"
export API_TOKEN="eyJ..."
export AGENT_ID="0d55f1bc-2b38-4c20-98b2-2b0f12345678"
# Opcionales (ya incluyen valores por defecto):
export CUSTOM_TOOL_IDENTIFIER="custom.http.workflow"
export CUSTOM_TOOL_URL="https://app.getsupervisor.ai/webhook/8820eb01-c309-46db-8f8c-ffb1c3ad659d"
export CUSTOM_TOOL_API_KEY="127362"
export CUSTOMER_NAME="Ada Lovelace"

npx tsx examples/custom-tool-connection.ts
```

El script envía la metadata de conexión (URL, método, headers) y, sobre todo, almacena un `configSchema` específico del workspace dentro de `tool_agent_connections.metadata`. Ese esquema describe los campos que el agente debe solicitar (en el ejemplo `agentId` y `customerName`) y coincide con la acción `triggerWorkflow` que expone el seed `custom.http.workflow`. Después de conectar la tool ejecuta `client.tools.execute('custom.http.workflow', { action: 'triggerWorkflow', args: { ... } })` respetando el schema definido en tu metadata.

## Configuración

```ts
createClient({
  baseUrl: '...',
  headers: { Authorization: 'Bearer ...' },
  // opcional: usa API Keys en lugar de Bearer
  // apiKey: 'sk_live_...',
  timeoutMs: 35000,
  retry: { maxRetries: 2, baseDelayMs: 300 },
  workspaceId: 'ws-123',
});
```

### Workspaces dinámicos

Puedes fijar el workspace al crear el cliente, actualizarlo en caliente o delegar en una función:

> Nota: los recursos _scoped por workspace_ (por ejemplo `client.agents.*`) requieren un workspace seleccionado. Si `client.workspace.get()` retorna `undefined`, el SDK lanzará un error antes de hacer la request.

```ts
const client = createClient({
  baseUrl,
  headers: { Authorization: 'Bearer ...' },
  // opción estática
  workspaceId: 'ws-123',
});

// consultar el workspace actual
client.workspace.get(); // 'ws-123'

// actualizarlo sin reconstruir el cliente
client.workspace.set('ws-456');

// o delegar en un getter dinámico (por ejemplo, almacenamiento local)
client.workspace.useGetter(
  () => sessionStorage.getItem('workspace') ?? undefined,
);

// crear un cliente aislado para otro workspace
const scoped = client.workspace.scoped('ws-789');
await scoped.agents.get('agentId');
```

## Gestión de API Keys

Los endpoints `/v1/api-keys` permiten listar, crear y revocar credenciales por workspace. Desde la versión 1.10 el SDK expone un namespace dedicado para gestionar el ciclo de vida completo de las claves sin escribir boilerplate adicional.

> También puedes consumir el CRUD de agentes con API Keys pasando `apiKey` a `createClient` o usando `client.auth.setApiKey('sk_...')`. El SDK enviará el header `X-API-Key` automáticamente en cada llamada.

### Cliente dedicado: `client.apiKeys`

```ts
import { createClient } from '@getsupervisor/speech-analytics-sdk';

const client = createClient({
  baseUrl: 'https://app.getsupervisor.ai',
  headers: { Authorization: `Bearer ${token}` },
  workspaceId: 'ws-123',
});

// 1. Listar credenciales activas
const keys = await client.apiKeys.list();

// 2. Emitir una nueva credencial (recuerda guardar el valor completo)
const created = await client.apiKeys.create({
  name: 'Backend Integration',
  environment: 'production',
  scopes: ['agents:read', 'api-keys:read'],
});
console.log(created.key); // sólo se entrega una vez

// 3. Revelar el valor completo cuando necesites consultarlo de nuevo
const revealed = await client.apiKeys.show(created.id);
console.log(revealed.key); // también incluye metadatos actualizados

// 4. Revocar cuando deje de ser necesaria
await client.apiKeys.revoke(created.id);
```

Cada entrada (`ApiKeySummary`) incluye `id`, `name`, `environment`, `scopes`, fechas de auditoría y un `keyPreview` para identificar la credencial sin exponerla por completo.

### Usar API Keys dinámicas

Puedes rotar claves en caliente con los helpers de autenticación:

```ts
client.auth.setApiKey('sk_prod_live');

client.auth.useApiKey(() => readSecretFromVault());

client.auth.clearApiKey();
```

El SDK enviará siempre el header `X-API-Key`; si lo borras, retomará la autenticación configurada en `headers`.

### Alternativa low-level con `createHttp`

Si necesitas controlar manualmente las peticiones (por ejemplo, en un entorno con fetch custom), puedes reutilizar la misma librería interna:

```ts
import { createHttp } from '@getsupervisor/speech-analytics-sdk';

const http = createHttp({
  baseUrl: 'https://app.getsupervisor.ai',
  headers: { Authorization: `Bearer ${token}` },
  workspaceId: 'ws-123',
});

const { key, ...metadata } = await http
  .doFetch(`${http.base}/v1/api-keys`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ name: 'Automation', scopes: ['agents:read'] }),
  })
  .then((res) => res.json());
```

### Buenas prácticas

- Segmenta las API Keys por entorno (`production`, `sandbox`, `staging`, `development`) y flujo de negocio.
- Registra un owner o nota descriptiva en `name` para acelerar auditorías.
- Almacena el valor completo (`key`) en un gestor de secretos y evita incluirlo en logs o repositorios.
- Programa rotaciones periódicas y revoca claves cuando detectes inactividad o incidentes.

### Scopes disponibles (octubre 2025)

| Scope                      | Permiso principal                                                         |
| -------------------------- | ------------------------------------------------------------------------- |
| `agents:read`              | Listar y consultar agentes del workspace.                                 |
| `agents:write`             | Crear/actualizar/eliminar agentes y gestionar su configuración operativa. |
| `agent-versions:read`      | Listar y consultar versiones de un agente.                                |
| `agent-versions:write`     | Crear, clonar, restaurar y publicar versiones de un agente.               |
| `agent-instructions:read`  | Leer instrucciones (a nivel agente y por versión).                        |
| `agent-instructions:write` | Crear, actualizar o borrar instrucciones personalizadas.                  |
| `agent-blueprints:read`    | Consultar el blueprint (personalidad) de una versión de agente.           |
| `agent-blueprints:write`   | Editar blueprint: personalidad, reglas, audiencia, objetivos, etc.        |
| `blueprint-stages:read`    | Listar y consultar stages del blueprint.                                  |
| `blueprint-stages:write`   | Crear, actualizar o reordenar stages del blueprint.                       |
| `stage-triggers:read`      | Consultar triggers que conectan stages y sus condiciones.                 |
| `stage-triggers:write`     | Crear, actualizar o eliminar triggers entre stages.                       |
| `agent-schedules:read`     | Consultar horario semanal y excepciones vigentes.                         |
| `agent-schedules:write`    | Crear o modificar horarios y excepciones.                                 |
| `campaigns:read`           | Consultar campañas y sus ejecuciones.                                     |
| `campaigns:write`          | Crear y disparar campañas batch.                                          |
| `calls:read`               | Consultar llamadas (Speech Analytics) del workspace.                      |
| `catalogs:read`            | Navegar catálogos globales y por workspace (idiomas, tonos, voces, etc.). |
| `catalogs:write`           | Registrar o ajustar ítems de catálogo.                                    |
| `tools:read`               | Descubrir tools disponibles, recursos y capacidades declaradas.           |
| `tools:connections:read`   | Listar conexiones configuradas entre agentes y tools.                     |
| `tools:connections:write`  | Crear o actualizar conexiones entre agentes y tools.                      |
| `tools:execute`            | Ejecutar acciones de una tool.                                            |
| `webhooks:read`            | Listar webhooks y sus suscripciones activas.                              |
| `webhooks:write`           | Crear, actualizar o eliminar webhooks y suscripciones.                    |
| `api-keys:read`            | Listar credenciales existentes y revelar su valor.                        |
| `api-keys:write`           | Emitir o revocar API Keys.                                                |
| `workspaces:read`          | Consultar workspaces disponibles y sus metadatos.                         |

Consulta el OpenAPI (`docs/api-spec/openapi.yaml`) para validar scopes adicionales (por ejemplo, específicos de conocimientos o teléfonos) y su mapeo exacto por operación.

## Novedades (octubre 2025)

- **Guías de uso para voice.calls**: las conexiones creadas con `client.tools.connect('voice.calls', ...)` ahora devuelven valores predeterminados en `descriptionUsage` y `usageExample` (ver `docs/mcp/tools_execution.md`).
- **Gestión de API Keys desde el SDK**: `client.apiKeys` expone helpers tipados para listar, crear y revocar credenciales con el mismo manejo de cabeceras, timeouts y reintentos del resto del cliente.
- **Scopes documentados**: tabla de scopes disponibles y recomendaciones para definir permisos mínimos por workspace.
- **Blueprint stages y triggers**: nuevos helpers `client.agents.stages(agentId)` y `stages.triggers(stageId)` para CRUD completo y reordenamiento del flujo conversacional.
- **Mejoras de gobernanza**: se incorporaron pautas de rotación, almacenamiento seguro y segmentación por entorno directamente en la guía del SDK.
- **Catálogos**: nuevo namespace `client.catalogs` con CRUD y paginación para `language`, `message_style`, `tone_style`, `tag` y `voice`.
- **Versiones de agente**: namespace `client.agentVersions` con operaciones para crear, clonar, publicar y gestionar instrucciones por versión.
- **Horarios del agente**: namespaces `client.agentSchedule` y `client.agentScheduleExceptions` para configurar disponibilidad semanal y excepciones por fecha.

## Errores tipados y reintentos

Los errores siguen modelados como `HttpError`, `TimeoutError` y `NetworkError`. Puedes configurar la política de reintentos con `retry: { maxRetries, baseDelayMs }` en el `createClient`.

## Compatibilidad

- Soporta ESM y CJS.
- Compatible con navegadores y Node.js (requiere `fetch` y `FormData` disponibles en el entorno).