---
name: zeph-crm
description: >
  CRM de ventas con leads, contactos, empresas, oportunidades, actividades y pipeline.
  Permite gestionar el flujo comercial completo: capturar leads, crear contactos y empresas,
  mover oportunidades por el pipeline, y registrar actividades como tareas, llamadas y reuniones.
metadata:
  version: "1.0.0"
  author: Zeph
  auth: bearer
  scopes:
    - leads:read
    - leads:write
    - contacts:read
    - contacts:write
    - companies:read
    - companies:write
    - opportunities:read
    - opportunities:write
    - activities:read
    - activities:write
    - pipeline:read
    - emails:read
    - emails:write
    - sequences:read
    - sequences:write
    - campaigns:read
---

# Zeph CRM Skill

## Config

- **BASE_URL**: `https://www.zephcrm.com/api/v1`
- **Auth**: Bearer token (API key). Header: `Authorization: Bearer <API_KEY>`
- La API key se crea en Settings > API Keys del CRM.

## Commands

### Leads

**Listar leads** — buscar leads, ver leads por estado o periodo
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/leads?search=QUERY&status=NEW&created_after=2025-01-01&created_before=2025-01-31&limit=20"
```
Filtros: search, status (NEW, CONTACTED, QUALIFIED, CONVERTED, LOST), contact_id (uno o varios ids separados por coma, hasta 100), created_after (ISO date), created_before (ISO date), tag (nombres separados por coma), industry (nombres separados por coma), custom.<campo>=<valor> (igualdad exacta sobre customFields; a,b = cualquiera; varias claves = AND; true/false y números matchean también el booleano o el número guardado).
Cada lead trae `contactId`, `companyId`, `tags` (id, name, color) y `companyRef` (empresa vinculada: website, employeeCount, industry, country).

**Obtener lead por ID**
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/leads/LEAD_ID"
```

**Crear lead** — registrar nuevo prospecto (requiere `leads:write`)
```bash
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"NAME","lastName":"LAST","email":"EMAIL","company":"COMPANY","phone":"PHONE","status":"NEW","source":"SOURCE_NAME","sourceDetail":"DETAIL"}' \
  "https://www.zephcrm.com/api/v1/leads"
```
Campos requeridos: firstName, lastName, email. Opcionales: phone, company, position, notes, status (NEW, CONTACTED, QUALIFIED, CONVERTED, LOST), source (nombre de la fuente — se crea automaticamente si no existe), sourceDetail (detalle adicional del origen), customFields (object), followUpDate (ISO date), assignedToId (UUID), tags (array de nombres — se crean si no existen), contactId (UUID del contacto del que sale el lead — si no mandás companyId, hereda su empresa), companyId (UUID de la empresa vinculada; gana sobre la del contacto).
Un campo que el endpoint no conoce responde 400 VALIDATION_ERROR (`details` dice cuál): no se descarta en silencio. Vale para POST y PATCH de leads y contactos, individuales y en lote.

**Promover un contacto a lead** — el lead queda vinculado al contacto (requiere `leads:read` y `leads:write`)
```bash
# 1. ¿cuáles del lote ya tienen lead? Una llamada para hasta 100 contactos.
curl -s -H "Authorization: Bearer $API_KEY" "https://www.zephcrm.com/api/v1/leads?contact_id=CONTACT_ID_1,CONTACT_ID_2&limit=100"
# 2. si no tiene, crearlo
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"NAME","lastName":"LAST","email":"EMAIL","contactId":"CONTACT_ID","source":"outbound"}' \
  "https://www.zephcrm.com/api/v1/leads"
```
El POST no deduplica por contacto: el paso 1 es lo que evita dos leads para la misma persona (la API no borra leads); creá sólo para los contactos cuyo id no aparece como `contactId` en la respuesta. El lead hereda la empresa del contacto (`companyRef` en la respuesta). Errores: 404 si el contacto (o el companyId) no es de la organización, 422 CONTACT_OPTED_OUT si se dio de baja.

**Actualizar lead** (requiere `leads:write`)
```bash
curl -s -X PATCH -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"CONTACTED","notes":"Updated notes"}' \
  "https://www.zephcrm.com/api/v1/leads/LEAD_ID"
```

**Actualizar leads en lote** — hasta 100 en una llamada, todo o nada (requiere `leads:write`)
```bash
curl -s -X PATCH -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ids":["ID1","ID2"],"data":{"status":"CONTACTED","customFields":{"pipeline_status":"processing"},"addTags":["lote-7"]}}' \
  "https://www.zephcrm.com/api/v1/leads/bulk"
```
data admite: status, assignedToId, notes, followUpDate, customFields (merge por clave, null borra), addTags (agrega). Retorna { updated: N }.

En PATCH (individual y en lote) `customFields` se MERGEA con lo existente: sólo cambian las claves enviadas y `null` elimina una clave. `aiScore` lo calcula el CRM y no se escribe; guardá tu propio score en customFields.

### Contacts

**Listar contactos** — buscar, o seleccionar un lote para trabajar
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/contacts?custom.outbound_status=pending&email_opt_out=false&limit=100&sort=createdAt&order=asc"
```
Filtros: search, company_id, email_opt_out (true/false), created_after (ISO date), created_before (ISO date), tag (nombres separados por coma; repetir el parámetro = todos), custom.<campo>=<valor> (igualdad exacta sobre customFields; a,b = cualquiera; varias claves = AND; true/false y números matchean también el booleano o el número guardado).
Mandá SIEMPRE email_opt_out=false si vas a escribirle a alguien: quien se dio de baja no se trabaja.
Cada contacto trae `ownerId`, `emailOptOut`, `tags` (id, name, color), `customFields` y `companyRef` (empresa vinculada: website, employeeCount, industry, country — no hace falta pedir /companies/{id}).

**Obtener contacto por ID**
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/contacts/CONTACT_ID"
```

**Crear contacto** (requiere `contacts:write`). Email debe ser unico por organizacion.
```bash
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"NAME","lastName":"LAST","email":"EMAIL","phone":"PHONE","position":"CARGO","companyId":"COMPANY_ID"}' \
  "https://www.zephcrm.com/api/v1/contacts"
```
Campos requeridos: firstName, lastName.

**Actualizar contacto** (requiere `contacts:write`)
```bash
curl -s -X PATCH -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"position":"New Position"}' \
  "https://www.zephcrm.com/api/v1/contacts/CONTACT_ID"
```
Para dar de baja a alguien que pidió que no le escriban: `{"emailOptOut":true}`. Desde ahí POST /emails se niega a escribirle. Volver a darlo de alta (`false`) no se puede por API: 400.

**Actualizar contactos en lote** — hasta 100 en una llamada, todo o nada (requiere `contacts:write`). Para reclamar un lote antes de procesarlo.
```bash
curl -s -X PATCH -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ids":["ID1","ID2"],"data":{"customFields":{"outbound_status":"processing"},"addTags":["lote-7"]}}' \
  "https://www.zephcrm.com/api/v1/contacts/bulk"
```
data admite: customFields (merge por clave, null borra), addTags (agrega), ownerId. Retorna { updated: N }. Si un id no es de la organización: 404 con la lista y no se toca ninguno.

**Crear contactos en lote** — hasta 100, deduplicando por email sin distinguir mayúsculas (requiere `contacts:write`)
```bash
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contacts":[{"firstName":"NAME","lastName":"LAST","email":"EMAIL"}]}' \
  "https://www.zephcrm.com/api/v1/contacts/bulk"
```
Cada fila con los mismos campos que el POST individual. Un email que ya existe no es error. Retorna { created, existing, duplicate, results: [{ index, id, status, email }] }, una entrada por fila en el orden del body; status es created (nuevo), existing (ya estaba; id es el suyo) o duplicate (repite el email de otra fila del lote).

### Companies

**Listar empresas**
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/companies?search=QUERY&limit=20"
```

**Obtener empresa por ID**
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/companies/COMPANY_ID"
```

**Crear empresa** (requiere `companies:write`). Nombre se deduplica case-insensitive.
```bash
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"COMPANY NAME","website":"https://example.com","phone":"PHONE","city":"CITY"}' \
  "https://www.zephcrm.com/api/v1/companies"
```
Campo requerido: name.

**Actualizar empresa** (requiere `companies:write`)
```bash
curl -s -X PATCH -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"website":"https://new-site.com"}' \
  "https://www.zephcrm.com/api/v1/companies/COMPANY_ID"
```

### Opportunities

**Listar oportunidades** — ver pipeline, buscar deals, filtrar por periodo
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/opportunities?search=QUERY&created_after=2025-01-01&created_before=2025-01-31&limit=20"
```
Filtros: search, stage_id, contact_id, company_id, created_after (ISO date), created_before (ISO date), close_after (ISO date, por expectedCloseDate), close_before (ISO date).

**Obtener oportunidad por ID** — incluye etapa del pipeline, contacto y empresa
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/opportunities/OPP_ID"
```

**Crear oportunidad** (requiere `opportunities:write`). Usa list_pipeline_stages para obtener stageId.
```bash
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"DEAL TITLE","value":50000,"currency":"USD","stageId":"STAGE_ID","contactId":"CONTACT_ID","expectedCloseDate":"2025-12-31"}' \
  "https://www.zephcrm.com/api/v1/opportunities"
```
Campos requeridos: title, stageId.

**Actualizar oportunidad** (requiere `opportunities:write`). Cambiar stageId mueve la oportunidad en el pipeline.
```bash
curl -s -X PATCH -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stageId":"NEW_STAGE_ID","value":60000}' \
  "https://www.zephcrm.com/api/v1/opportunities/OPP_ID"
```

### Activities

**Listar actividades** — tareas, llamadas, reuniones, emails, notas
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/activities?type=TASK&status=PENDING&due_after=2025-01-01&limit=20"
```
Filtros: type (TASK, CALL, MEETING, EMAIL, NOTE), status (PENDING, COMPLETED), assigned_to (userId), due_after, due_before, contact_id, opportunity_id.

**Obtener actividad por ID**
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/activities/ACTIVITY_ID"
```

**Crear actividad** (requiere `activities:write`). Las tareas (TASK) requieren dueDate.
```bash
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"TASK","title":"Follow up call","dueDate":"2025-06-15T10:00:00Z","priority":"HIGH","contactId":"CONTACT_ID"}' \
  "https://www.zephcrm.com/api/v1/activities"
```
Campos requeridos: type, title. Tipos: TASK, CALL, MEETING, EMAIL, NOTE. Prioridades: LOW, MEDIUM, HIGH.

**Actualizar actividad** (requiere `activities:write`). Para completar, setea completedAt.
```bash
curl -s -X PATCH -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"completedAt":"2025-06-15T14:30:00Z"}' \
  "https://www.zephcrm.com/api/v1/activities/ACTIVITY_ID"
```

### Pipeline

**Listar etapas del pipeline** — obtener IDs de etapas para crear/mover oportunidades
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/pipeline-stages"
```
Retorna: id, name, order, color, probability, isDefault, isWon, isLost.

### Search

**Busqueda global** — busca en todas las entidades segun los scopes de la API key
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/search?q=QUERY&types=leads,contacts&limit=5"
```
Parametros: q (requerido, min 2 chars), types (filtro por entidad, comma-separated), limit (max 20).
Retorna resultados agrupados: { leads: [...], contacts: [...], companies: [...], opportunities: [...], activities: [...] }

### Sequences

**Listar secuencias** — secuencias de seguimiento de la organización (requiere `sequences:read`)
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/sequences"
```
Cada secuencia trae id, name, isActive, reviewMode, language, stepCount y activeEnrollments. Las secuencias propias de una campaña no aparecen: se manejan desde la campaña.

**Obtener secuencia por ID** — incluye los pasos con su modo (AI, TEMPLATE, MANUAL) y delays
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/sequences/SEQUENCE_ID"
```

**Enrolar contactos** — hasta 100 por llamada, sin ids repetidos (requiere `sequences:write`)
```bash
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contactIds":["CONTACT_ID_1","CONTACT_ID_2"]}' \
  "https://www.zephcrm.com/api/v1/sequences/SEQUENCE_ID/enrollments"
```
El seguimiento es del contacto: enrolá por `contactIds`. `leadIds` sigue aceptándose pero está deprecado (la respuesta lleva el header `Deprecation: true`). Un campo desconocido responde 400.
Retorna 201 { enrolled, skipped: [{ leadId, contactId, code, reason }] }. code: NOT_FOUND, NO_EMAIL, OPT_OUT, ALREADY_ACTIVE (ya tiene un seguimiento activo), LEAD_CLOSED. Los omitidos no son error: revisá `code` en vez de reintentar.
Errores: 422 SEQUENCE_OWNED_BY_CAMPAIGN (la secuencia es de una campaña), 422 ENROLLMENT_FAILED (secuencia inactiva, sin pasos o sin casilla conectada).

**Listar enrollments** — estado de los seguimientos de un contacto o de una campaña (requiere `sequences:read`)
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/enrollments?contactId=CONTACT_ID"
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/enrollments?campaignId=CAMPAIGN_ID&status=REPLIED"
```
Filtros (al menos uno de los cuatro primeros): contactId, campaignId, sequenceId, leadId (404 si el lead no existe), status (ACTIVE, WAITING_APPROVAL, REPLIED, COMPLETED, CANCELLED, FAILED). Retorna los 50 más recientes.
Cada enrollment trae status, currentStep, totalSteps, nextSendAt, repliedAt, repliedAfterStep, campaignId, `contact` (id, firstName, lastName, email) y `lead`, que puede venir en null: el contacto no necesita lead para tener seguimiento.
De una campaña que el dueño de la key no ve (un ADMIN ve todas; otro rol, sólo las que creó), campaignId y el id y el nombre de su secuencia vienen en null, y filtrar por ella responde 404.

Para cortar un seguimiento en curso: DELETE /enrollments/{id} (requiere `sequences:write`). No se le envían más emails a esa persona. Uno que vino de una campaña lo cancela sólo un dueño de key que la creó o es ADMIN; si no, responde 404.

### Campaigns

**Listar campañas** — métricas con tasas ya calculadas (requiere `campaigns:read`)
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/campaigns?status=COMPLETED&limit=20"
```
Cada campaña trae version (1 o 2), totalSent, totalReplied, openRate, clickRate, replyRate, followUpSequence y followUp (conteos de los seguimientos que puso en marcha, null si no tiene).
`steps`: en las campañas v2, un elemento por paso { order, mode (AI, TEMPLATE, MANUAL), sent, repliedAfter }; en las v1 viene vacío. `subject` es null cuando el paso 1 de una v2 es AI: cada email lleva su propio asunto.

**Destinatarios de una campaña** — a quién ir a buscar
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/campaigns/CAMPAIGN_ID/recipients?replied=true"
```
`replied=true` devuelve sólo a quienes respondieron. Cada destinatario trae email, contactId, contactName, leadId (el lead del seguimiento o, si no tiene, el último lead del contacto), leadStatus, skippedReason y `enrollment` ({ id, status, currentStep } o null si no se lo enroló).

### Emails

**Enviar un email** — un mail personalizado, de a uno, desde la casilla conectada del dueño de la API key (requiere `emails:write`)
```bash
curl -s -X POST -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"lead","subject":"SUBJECT","body":"<p>HTML</p>","leadId":"LEAD_ID","idempotencyKey":"outbound-LEAD_ID-RUN_ID"}' \
  "https://www.zephcrm.com/api/v1/emails"
```
Campos requeridos: to, subject, body (HTML). `to` es una dirección o un atajo: "lead" (el email del lead de leadId), "contact" (el de contactId) u "owner" (tu propia casilla, para un preview). Opcionales: toName, cc, bcc, leadId, contactId, opportunityId (vinculan el envío al timeline), emailAccountId, threadId + inReplyTo (para responder en un hilo previo), idempotencyKey.
Mandá SIEMPRE idempotencyKey: la misma clave devuelve el envío original con 200 en vez de mandar otra vez, así un reintento no duplica el email. 409 IN_PROGRESS si otro request con esa clave está enviando ahora.
Retorna el SentEmail con `threadId`.
Errores: 422 EMAIL_OPT_OUT (el destinatario se dio de baja: el contacto de contactId, el contacto del lead de leadId, o un contacto con la dirección de `to` — no reintentar, descartar a la persona), 422 NO_EMAIL_ACCOUNT (el dueño de la key no tiene Gmail/Outlook conectado), 502 SEND_FAILED (el proveedor rechazó el mensaje).

**Emails enviados a alguien** — qué se le mandó ya, antes de volver a escribirle (requiere `emails:read`)
```bash
curl -s -H "Authorization: Bearer $API_KEY" \
  "https://www.zephcrm.com/api/v1/emails?leadId=LEAD_ID"
```
Filtros (al menos uno): leadId, contactId, opportunityId, to. Retorna los 50 más recientes con subject, toEmail, threadId, sentAt, openedAt, openCount.

## Notes

- La única eliminación de la API es cancelar un enrollment; el resto se hace desde la UI.
- Todas las listas usan paginacion por cursor. El response incluye `nextCursor` si hay mas resultados.
- Los IDs son UUIDs (cuid).
- Las fechas se envian y reciben en formato ISO 8601.
- Rate limit: depende de la configuracion de la API key (default 100 req/min).
