# Gestión de Workers

> Que son los Workers y como funcionan en Plazbot

Source: https://docs.plazbot.com/kb/agentes-definicion

Los Workers son funciones TypeScript que extienden las capacidades de tu workspace en Plazbot. Permiten ejecutar logica personalizada que se integra con el Agente de IA, responde a eventos externos, o se ejecuta de forma programada.

## Para que sirven

- Conectar tu agente con APIs externas (inventario, CRM, pagos).
- Procesar webhooks de servicios como Stripe, Shopify o MercadoPago.
- Ejecutar tareas programadas (reportes, sincronizaciones, alertas).
- Automatizar operaciones sobre contactos, conversaciones y variables.

* * *

## Requisitos previos

> **Advertencia:** **Antes de usar workers, debes configurar los siguientes secrets obligatorios** desde el tab **Developers > Secrets** en el dashboard. Sin estos secrets, ningun worker podra ejecutarse.
> 
> Secret
> 
> Descripcion
> 
> `PLZ_API_KEY`
> 
> Token de acceso a la API de Plazbot. Permite que los workers interactuen con contactos, conversaciones, templates y demas recursos del workspace.
> 
> `PLZ_ZONE`
> 
> Region del workspace (`LA` para Latinoamerica, `EU` para Europa). Determina a que servidor de API se conectan los workers.
> 
> Para configurarlos facilmente, haz click en el boton **"Configurar Secrets de Plazbot"** que aparece en el tab Secrets. Este genera el token automaticamente y detecta la zona de tu workspace.

* * *

## Estructura de un Worker

Cada worker es un archivo `.ts` que importa una funcion `define*` del SDK de Plazbot y exporta su configuracion junto con un handler.

```typescript

  name: 'mi-herramienta',
  reference: 'Descripcion de lo que hace esta herramienta',
  agents: ['id_del_agente'],
  parameters: [
    { name: 'query', type: 'string', description: 'Parametro requerido' }
  ],

  async run(payload, plz) {
    // Logica del worker
    return { resultado: 'ok' }
  }
})
```

**Partes principales:**

Parte

Descripcion

`import`

Importa la funcion define\* correspondiente al tipo de worker.

`name`

Nombre unico del worker dentro del workspace.

`reference`

Descripcion del worker. En tools, el agente usa esta descripcion para decidir cuando invocarlo.

`run(payload, plz)`

Funcion handler que contiene la logica. Recibe el payload y el contexto `plz`.

* * *

## Ciclo de vida

1. **Escribir** Crea un archivo `.ts` con la definicion del worker usando una de las funciones `define*`.
2. **Desplegar** Usa el CLI (`plazbot workers deploy mi-worker.ts`) o el editor del dashboard para subir el worker al workspace.
3. **Ejecutar** El worker se ejecuta automaticamente segun su tipo: el agente lo invoca (tool), un cron lo dispara (schedule/sync), un HTTP request lo activa (webhook), o se ejecuta bajo demanda (worker).

* * *

## Integración con el Agente de IA

Los workers se integran con el Agente de IA a traves de las **Acciones del Agente**. Dentro de la configuracion de acciones, puedes usar el tipo `action.worker` para invocar un worker cuando el agente detecta una intencion.

Advertencia ⚠️: El tipo action.worker en las acciones del agente solo soporta workers de tipo worker (definidos con defineWorker). No puedes invocar directamente un tool, schedule o webhook desde una accion del agente.

Los workers de tipo **tool** (`defineTool`) se integran de forma diferente: se registran directamente como herramientas del agente mediante el campo `agents`, y el agente los invoca automaticamente durante la conversacion cuando lo considera necesario.

* * *

## Campos comunes del define\*

Todos los tipos de workers comparten estos campos de configuracion:

Campo

Tipo

Requerido

Descripcion

`name`

`string`

Si

Nombre unico del worker. Se usa como identificador en el workspace.

`reference`

`string`

No

Descripcion del proposito del worker.

Cada tipo agrega campos adicionales segun su funcion. Consulta la pagina de [Tipos](/guides/agents/workers/tipos) para ver los campos especificos.

* * *

## Runtime

Los workers se ejecutan en un entorno aislado de Plazbot con acceso al contexto `plz`, que provee metodos para interactuar con la plataforma. El codigo TypeScript se compila a JavaScript antes del despliegue.

Tip: No necesitas instalar dependencias externas. El runtime provee automaticamente el SDK plz con acceso a contactos, WhatsApp, templates, key-value store y mas. Consulta la pagina de Contexto para ver todos los métodos disponibles.

## Tipos de Workers

Existen 5 tipos de workers, cada uno diseñado para un caso de uso diferente. El tipo se define por la funcion `define*` que utilices.

## Tabla comparativa

Tipo

Funcion

Trigger

Uso principal

**Tool**

`defineTool`

El agente IA lo invoca

Consultas a APIs durante conversaciones

**Worker**

`defineWorker`

Bajo demanda (API, acciones, otros workers)

Logica de negocio reutilizable

**Sync**

`defineSync`

Cron programado

Sincronizacion periodica de datos

**Schedule**

`defineSchedule`

Cron programado

Tareas automaticas recurrentes

**Webhook**

`defineWebhook`

HTTP request externo

Recibir eventos de servicios externos

* * *

## Tool (`defineTool`)

Herramientas que el agente IA puede invocar durante una conversacion. Ideales para consultas a APIs externas, busquedas en base de datos, o cualquier operacion en tiempo real.

**Campos de configuracion:**

Campo

Tipo

Descripcion

`name`

`string`

Nombre unico del tool.

`reference`

`string`

Descripcion para que el agente entienda cuando usarlo.

`agents`

`string[]`

IDs de los agentes que pueden invocar este tool.

`parameters`

`array`

Parametros que el agente debe recopilar del usuario.

**Signature del handler:**

```typescript
async run(payload, plz) {
  // payload contiene los parametros + contactId
  // Retorna datos que el agente usara en su respuesta
  return { ... }
}
```

**Cada parametro tiene:**

Campo

Tipo

Descripcion

`name`

`string`

Nombre del parametro.

`type`

`string`

Tipo: `string`, `number`, `array`, `boolean`.

`description`

`string`

Descripcion para que el agente sepa que pedir al usuario.

`example`

`string`

Ejemplo de pregunta que el agente puede hacer.

**Ejemplo:**

```typescript

  name: 'consultar-stock',
  reference: 'Consulta disponibilidad de productos en inventario',
  agents: ['agent_abc123'],
  parameters: [
    { name: 'producto', type: 'string', description: 'Nombre del producto' }
  ],

  async run(payload, plz) {
    const res = await fetch(`${plz.env.API_URL}/stock?q=${payload.producto}`)
    return await res.json()
  }
})
```

* * *

## Worker (`defineWorker`)

Funciones de proposito general invocadas bajo demanda. Se ejecutan desde automatizaciones, acciones del agente (`action.worker`), otros workers o la API.

**Campos de configuracion:**

Campo

Tipo

Descripcion

`name`

`string`

Nombre unico del worker.

`reference`

`string`

Descripcion del proposito.

**Signature del handler:**

```typescript
async run(payload, plz) {
  // payload contiene los datos enviados al ejecutar
  // payload.contactId y payload.contact disponibles si se invoca desde una accion
  return { ... }
}
```

**Ejemplo:**

```typescript

  name: 'notificar-equipo',
  reference: 'Envia notificacion al equipo por Slack',

  async run(payload, plz) {
    const { contactId, mensaje } = payload

    plz.log.info('Enviando notificacion', { contactId })

    await fetch(plz.env.SLACK_WEBHOOK, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ text: mensaje })
    })

    return { success: true }
  }
})
```

> **Tip:** Este es el unico tipo de worker que puede ser invocado desde las acciones del agente de IA usando `action.worker`.

* * *

## Sync (`defineSync`)

Sincronizaciones programadas que mantienen datos actualizados entre la plataforma y sistemas externos. Se ejecutan en intervalos regulares via cron.

**Campos de configuracion:**

Campo

Tipo

Descripcion

`name`

`string`

Nombre unico del sync.

`reference`

`string`

Descripcion del proposito.

`cron`

`string`

Expresion cron que define la frecuencia.

`timezone`

`string`

Zona horaria para el cron (ej: `America/Mexico_City`).

**Signature del handler:**

```typescript
async run(plz) {
  // No recibe payload - se ejecuta automaticamente
  return { ... }
}
```

**Ejemplo:**

```typescript

  name: 'sync-crm-contacts',
  reference: 'Sincroniza contactos modificados en el CRM',
  cron: '*/30 * * * *',
  timezone: 'America/Lima',

  async run(plz) {
    const contacts = await plz.contacts.list({ limit: 100 })
    // ... logica de sincronizacion
    return { synced: 15 }
  }
})
```

* * *

## Schedule (`defineSchedule`)

Tareas programadas que se ejecutan automaticamente en horarios definidos. Utiles para reportes, limpiezas, notificaciones periodicas y mas.

**Campos de configuracion:**

Campo

Tipo

Descripcion

`name`

`string`

Nombre unico del schedule.

`reference`

`string`

Descripcion del proposito.

`cron`

`string`

Expresion cron que define cuando se ejecuta.

`timezone`

`string`

Zona horaria para el cron.

**Signature del handler:**

```typescript
async run(plz) {
  // No recibe payload - se ejecuta automaticamente
  return { ... }
}
```

> **Nota:** La diferencia entre `defineSync` y `defineSchedule` es conceptual: sync se usa para sincronizar datos entre sistemas, schedule para tareas autonomas. Tecnicamente funcionan igual.

**Ejemplo:**

```typescript

  name: 'reporte-diario',
  reference: 'Envia resumen diario de chats pendientes',
  cron: '0 22 * * 1-5',
  timezone: 'America/Mexico_City',

  async run(plz) {
    const contacts = await plz.contacts.list({ limit: 200 })
    const pending = contacts.filter(c => !c.isSolved)
    // ... enviar reporte
    return { pending: pending.length }
  }
})
```

* * *

## Webhook (`defineWebhook`)

Endpoints HTTP que reciben eventos de servicios externos en tiempo real. Al desplegarse, se genera una URL unica que puedes configurar en el servicio externo.

**Campos de configuracion:**

Campo

Tipo

Descripcion

`name`

`string`

Nombre unico del webhook.

`reference`

`string`

Descripcion del proposito.

`method`

`string`

Metodo HTTP aceptado: `POST`, `GET`, `PUT`.

**Signature del handler:**

```typescript
async run(payload, plz) {
  // payload contiene el body del request HTTP
  return { ... }
}
```

**Ejemplo:**

```typescript

  name: 'webhook-pagos',
  reference: 'Recibe eventos de pago de Stripe',
  method: 'POST',

  async run(payload, plz) {
    if (payload.type !== 'checkout.session.completed') {
      return { ignored: true }
    }

    const email = payload.data.object.customer_details.email
    const matches = await plz.contacts.search({ email })

    if (matches.length > 0) {
      await plz.contacts.addTag(matches[0].id, 'cliente-pagado')
    }

    return { success: true }
  }
})
```

> **Tip:** Al desplegar un webhook, el sistema genera automaticamente una URL. Puedes verla en el dashboard o con `plazbot workers list`. Usa esa URL para configurar el webhook en el servicio externo (Stripe, Shopify, etc).

* * *

## Expresiones Cron

Los workers de tipo `sync` y `schedule` usan expresiones cron para definir su frecuencia de ejecucion.

Expresion

Significado

`*/5 * * * *`

Cada 5 minutos

`*/30 * * * *`

Cada 30 minutos

`0 * * * *`

Cada hora en punto

`0 9 * * *`

Todos los dias a las 9:00

`0 22 * * 1-5`

Lunes a viernes a las 22:00

`0 0 * * 0`

Domingos a medianoche

`0 */6 * * *`

Cada 6 horas

El campo `timezone` determina en que zona horaria se evalua la expresion cron. Valores comunes:

- `America/Mexico_City`
- `America/Lima`
- `America/Bogota`
- `America/Santiago`
- `America/Argentina/Buenos_Aires`

## Ejemplos de Workers

A continuacion se presentan ejemplos reales de workers para cada tipo principal. Estos mismos ejemplos estan disponibles en el tab "Ejemplos" del modulo Developers en el dashboard.

* * *

## Tool: Consultar inventario

El agente IA consulta productos disponibles en stock desde un ERP o base de datos externa.

```typescript

  name: 'consultar-inventario',
  reference: 'Busca productos disponibles en stock por nombre o SKU',
  agents: ['id_agent_1'],
  parameters: [
    { name: 'query', type: 'string', description: 'Nombre o SKU del producto', example: 'Que producto estas buscando?' }
  ],

  async run(payload, plz) {
    const apiUrl = plz.env.INVENTORY_API_URL
    const apiKey = plz.env.INVENTORY_API_KEY

    const res = await fetch(`${apiUrl}/products/search?q=${payload.query}`, {
      headers: { 'Authorization': `Bearer ${apiKey}` }
    })

    const products = await res.json()

    return products.map(p => ({
      nombre: p.name,
      precio: `$${p.price}`,
      stock: p.stock > 0 ? `${p.stock} disponibles` : 'Agotado',
      sku: p.sku
    }))
  }
})
```

**Como funciona:**

- El agente detecta que el usuario pregunta por un producto.
- Recopila el parametro `query` del usuario.
- Ejecuta el tool, que consulta la API de inventario.
- El agente responde con la informacion de los productos encontrados.

**Secrets necesarios:** `INVENTORY_API_URL`, `INVENTORY_API_KEY`

* * *

## Worker: Notificar a Slack

Notifica a Slack cuando un contacto solicita atencion humana. Demuestra el uso de `plz.log` para registrar cada paso.

```typescript

  name: 'notificar-slack',
  reference: 'Notifica a Slack cuando un contacto solicita atencion humana',

  async run(payload, plz) {
    const { contactId, contact } = payload

    plz.log.info('Worker iniciado', { contactId })

    // Validar configuracion
    const webhookUrl = plz.env.SLACK_WEBHOOK_URL
    if (!webhookUrl) {
      plz.log.error('SLACK_WEBHOOK_URL no configurado en secrets')
      return { success: false, error: 'Webhook no configurado' }
    }

    // Obtener datos del contacto si no vienen en el payload
    let contactName = contact?.name || 'Sin nombre'
    let contactPhone = contact?.phoneNumber || ''

    if (!contact && contactId) {
      plz.log.info('Consultando datos del contacto...')
      const contactData = await plz.contacts.get(contactId)
      contactName = contactData.name || 'Sin nombre'
      contactPhone = contactData.phoneNumber || ''
    }

    plz.log.info('Contacto identificado', { contactName, contactPhone })

    // Enviar notificacion a Slack
    const message = `*Nuevo lead solicita atencion humana*\n- Nombre: ${contactName}\n- Telefono: ${contactPhone}\n- Ultimo mensaje: ${contact?.lastMessage || 'N/A'}`

    const response = await fetch(webhookUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ text: message })
    })

    if (!response.ok) {
      plz.log.error('Error enviando a Slack', await response.text())
      return { success: false }
    }

    plz.log.info('Notificacion enviada a Slack exitosamente')

    return { success: true, contactName }
  }
})
```

**Como funciona:**

- Se invoca desde una accion del agente (`action.worker`) cuando el usuario pide hablar con un humano.
- Usa `plz.log.info` y `plz.log.error` para registrar cada paso (visibles en el tab Logs).
- Obtiene datos del contacto y envia un mensaje a Slack con la informacion.

**Secrets necesarios:** `SLACK_WEBHOOK_URL`

* * *

## Webhook: Pagos de Stripe

Recibe notificaciones de pagos exitosos de Stripe y actualiza el contacto automaticamente.

```typescript

  name: 'webhook-stripe-payments',
  reference: 'Recibe eventos de pago de Stripe y actualiza contactos',
  method: 'POST',

  async run(payload, plz) {
    const event = payload

    if (event.type !== 'checkout.session.completed') {
      return { ignored: true, reason: `Evento ${event.type} ignorado` }
    }

    const session = event.data.object
    const email = session.customer_details.email

    // Buscar contacto por email y actualizar
    const matches = await plz.contacts.search({ email })

    if (matches.length > 0) {
      const contactId = matches[0].id

      // Actualizar variables custom del contacto
      await plz.contacts.setVariable(contactId, 'ctc_ultimo_pago', `$${session.amount_total / 100}`)
      await plz.contacts.setVariable(contactId, 'ctc_fecha_pago', new Date().toISOString())
      await plz.contacts.setVariable(contactId, 'ctc_stripe_customer', session.customer)

      // Agregar tag
      await plz.contacts.addTag(contactId, 'cliente-pagado')
    }

    return {
      success: true,
      email,
      amount: session.amount_total / 100,
      currency: session.currency
    }
  }
})
```

**Como funciona:**

- Stripe envia un POST a la URL del webhook cada vez que ocurre un evento.
- El worker filtra solo eventos `checkout.session.completed`.
- Busca el contacto por email y actualiza sus variables custom con los datos del pago.
- Agrega el tag `cliente-pagado` para segmentacion.

**Configuracion en Stripe:**

1. Despliega el webhook con `plazbot workers deploy webhook-stripe.ts`.
2. Copia la URL generada (visible con `plazbot workers list`).
3. Configura esa URL en Stripe Dashboard > Webhooks > Add endpoint.

* * *

## Schedule: Reporte nocturno

Envia un resumen diario de conversaciones pendientes al equipo por Slack, de lunes a viernes a las 10pm.

```typescript

  name: 'reporte-nocturno',
  reference: 'Envia resumen diario de chats pendientes por Slack',
  cron: '0 22 * * 1-5',
  timezone: 'America/Mexico_City',

  async run(plz) {
    // Obtener contactos y filtrar los no resueltos
    const allContacts = await plz.contacts.list({ limit: 200 })
    const pendingChats = allContacts.filter(c => !c.isSolved)

    const summary = {
      total: pendingChats.length,
      byAgent: {}
    }

    for (const chat of pendingChats) {
      const agent = chat.assignedAgentName || 'Sin asignar'
      summary.byAgent[agent] = (summary.byAgent[agent] || 0) + 1
    }

    const agentLines = Object.entries(summary.byAgent)
      .map(([agent, count]) => `  - ${agent}: ${count} chats`)
      .join('\n')

    await fetch(plz.env.SLACK_WEBHOOK, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        text: `*Reporte nocturno*\n\nChats pendientes: *${summary.total}*\n\nPor agente:\n${agentLines}`
      })
    })

    return { sent: true, pending: summary.total }
  }
})
```

**Como funciona:**

- Se ejecuta automaticamente de lunes a viernes a las 22:00 (hora Mexico).
- Lista todos los contactos y filtra los no resueltos.
- Agrupa los chats pendientes por agente asignado.
- Envia un resumen formateado a Slack.

**Secrets necesarios:** `SLACK_WEBHOOK`

**Cron explicado:** `0 22 * * 1-5` = minuto 0, hora 22, cualquier dia del mes, cualquier mes, lunes(1) a viernes(5).
