# Referencia de la API (/es/api-reference/overview)

## URL base

```
https://api.bitelio.com
```

Todas las solicitudes van a esta URL base.

## Autenticación

Pasa tu clave de API como token bearer en el encabezado `Authorization`:

```bash
Authorization: Bearer YOUR_API_KEY
```

* **Clave secreta (sk\_\*)** — necesaria en todos los endpoints excepto `/v1/track`
* **Clave pública (pk\_\*)** — solo se acepta en `/v1/track`, para que puedas registrar eventos desde código del lado del cliente

## Cómo hacer solicitudes

### Enviar un correo transaccional

```bash
curl -X POST https://api.bitelio.com/v1/send \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "user@example.com",
    "subject": "Hello",
    "body": "<p>Your message here</p>"
  }'
```

### Registrar un evento

```bash
curl -X POST https://api.bitelio.com/v1/track \
  -H "Authorization: Bearer pk_your_public_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "event": "signed_up"
  }'
```

### Crear un contacto

```bash
curl -X POST https://api.bitelio.com/contacts \
  -H "Authorization: Bearer sk_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "subscribed": true,
    "data": {
      "firstName": "John",
      "plan": "pro"
    }
  }'
```

## Formato de respuesta

### Respuestas de éxito

Los endpoints públicos (`/v1/send`, `/v1/track`, `/v1/verify`) envuelven su payload en un sobre:

```json
{
  "success": true,
  "data": {
    "contact": "cnt_abc123",
    "event": "evt_xyz789",
    "timestamp": "2025-11-30T10:30:00.000Z"
  }
}
```

Los endpoints tipo panel (contactos, plantillas, campañas, segmentos, workflows, etc.) omiten el sobre `success`/`data` y devuelven directamente el recurso:

```json
{
  "id": "cnt_abc123",
  "email": "user@example.com",
  "createdAt": "2025-11-30T10:30:00.000Z"
}
```

Los endpoints de listado con paginación por cursor responden con:

```json
{
  "data": [ /* items */ ],
  "cursor": "def456",
  "hasMore": true,
  "total": 10000
}
```

### Respuesta de error

Cada error trae suficiente detalle como para depurarlo directamente:

```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "statusCode": 422,
    "requestId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "errors": [
      {
        "field": "email",
        "message": "Invalid email",
        "code": "invalid_string"
      }
    ],
    "suggestion": "One or more fields have incorrect types. Check that strings are quoted, numbers are unquoted, and booleans are true/false."
  },
  "timestamp": "2025-11-30T10:30:00.000Z"
}
```

**Campos del error:**

* `code` — identificador estable y legible por máquina sobre el que puedes ramificar tu código
* `message` — explicación del fallo en lenguaje llano
* `statusCode` — el estado HTTP
* `requestId` — único por solicitud; cítalo cuando escribas a soporte
* `errors` — desglose de validación por campo, presente cuando aplica
* `suggestion` — una pista sobre cómo resolver el problema

La [documentación de códigos de error](/es/api-reference/errors) cubre todos los códigos con ejemplos resueltos.

## Paginación

La paginación **basada en cursor** es la norma en los endpoints de listado:

```bash
GET /contacts?limit=100&cursor=abc123
```

**Parámetros:**

* `limit` — tamaño de página (por defecto: 20, máximo: 100)
* `cursor` — el valor `cursor` devuelto por la página anterior

**Respuesta:**

```json
{
  "data": [ /* items */ ],
  "cursor": "def456",
  "hasMore": true,
  "total": 10000
}
```

Pasa el `cursor` de cada respuesta al parámetro de consulta `cursor` de la siguiente solicitud, y detente cuando `hasMore` venga `false`. Solo la primera página (la que se pide sin `cursor`) calcula `total`; las páginas siguientes reportan `total: 0` para que el listado siga siendo rápido.

Un puñado de endpoints (p. ej. `GET /segments/:id/contacts`) pagina **por número de página** en su lugar, tomando `page` y `pageSize` y devolviendo `total`, `page` y `pageSize` en la respuesta.

## Límites de tasa

Para mantener el servicio saludable para todos, Bitelio aplica límites razonables:

* **Envío de correos** — regulado por proyecto para proteger la entregabilidad.
* **Solicitudes de API** — 1000 solicitudes/minuto por proyecto.
* **Operaciones masivas** — se encolan automáticamente para procesamiento asíncrono.

Superar un límite te devuelve una respuesta `429 Too Many Requests`.

## Códigos de error

Los errores combinan códigos de estado HTTP estándar con identificadores de código legibles por máquina:

**400 Bad Request** — Los parámetros son inválidos o el cuerpo de la solicitud está mal formado

**401 Unauthorized** — Falta la clave de API o no es válida

**403 Forbidden** — No tienes acceso al recurso, o el proyecto está deshabilitado

**404 Not Found** — No existe ese recurso

**422 Unprocessable Entity** — La validación rechazó la solicitud (el array `errors` explica por qué)

**429 Too Many Requests** — Alcanzaste un límite de tasa

**500 Internal Server Error** — Algo falló de nuestro lado (envía a soporte el ID de la solicitud)

El catálogo completo, con pasos de resolución de problemas, está en la [documentación de códigos de error](/es/api-reference/errors).

## Endpoints de la API

Todos los endpoints que expone la API de Bitelio, agrupados por recurso.

### API pública

Las rutas `/v1/*` existen para el código de tu aplicación y toman payloads planos y desnormalizados. De todos los endpoints, solo `POST /v1/track` acepta una clave pública (`pk_*`).

| Método | Ruta         | Descripción                                                                                                                                   | Clave  |
| ------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
| POST   | `/v1/send`   | Envía un correo transaccional — uno o varios destinatarios, plantilla o contenido en línea, con adjuntos, encabezados y datos personalizados. | `sk_*` |
| POST   | `/v1/track`  | Registra un evento en un contacto, creando o haciendo upsert del contacto según haga falta. Dispara workflows.                                | `pk_*` |
| POST   | `/v1/verify` | Comprueba una dirección de correo: formato, registros MX, dominios desechables, posibles errores tipográficos.                                | `sk_*` |

### Contactos

| Método                    | Ruta                             | Descripción                                                                                                       |
| ------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| GET                       | `/contacts`                      | Lista contactos, con `search` (subcadena de correo), `limit` y `cursor` soportados.                               |
| POST                      | `/contacts`                      | Crea o hace upsert de un contacto identificado por correo. La respuesta incluye `_meta.isNew` y `_meta.isUpdate`. |
| GET                       | `/contacts/:id`                  | Obtiene un contacto.                                                                                              |
| PATCH                     | `/contacts/:id`                  | Cambia el correo, el estado de suscripción o los campos `data` de un contacto.                                    |
| DELETE                    | `/contacts/:id`                  | Elimina un contacto.                                                                                              |
| POST                      | `/contacts/lookup`               | Comprueba cuáles de hasta 500 correos ya existen, en una sola llamada.                                            |
| **Campos personalizados** |                                  |                                                                                                                   |
| GET                       | `/contacts/fields`               | Enumera campos estándar y personalizados, con tipos inferidos y porcentajes de cobertura.                         |
| GET                       | `/contacts/fields/:field/values` | Valores distintos de un campo personalizado — impulsa las interfaces de filtros de segmentos y workflows.         |
| GET                       | `/contacts/fields/:field/usage`  | Dónde aparece un campo personalizado (segmentos, campañas, workflows).                                            |
| DELETE                    | `/contacts/fields/:field`        | Elimina un campo personalizado de todos los contactos del proyecto.                                               |
| **Importación CSV**       |                                  |                                                                                                                   |
| POST                      | `/contacts/import`               | Sube un CSV (multipart, ≤ 5 MB). Se ejecuta como job encolado — devuelve un `jobId`.                              |
| GET                       | `/contacts/import/:jobId`        | Consulta un job de importación CSV.                                                                               |
| **Operaciones masivas**   |                                  |                                                                                                                   |
| POST                      | `/contacts/bulk-subscribe`       | Suscribe hasta 1000 contactos por ID. Encolado — devuelve un `jobId`.                                             |
| POST                      | `/contacts/bulk-unsubscribe`     | Da de baja hasta 1000 contactos por ID. Encolado.                                                                 |
| POST                      | `/contacts/bulk-delete`          | Elimina hasta 1000 contactos por ID. Encolado.                                                                    |
| GET                       | `/contacts/bulk/:jobId`          | Consulta un job masivo.                                                                                           |

### Plantillas

| Método | Ruta                       | Descripción                                                                      |
| ------ | -------------------------- | -------------------------------------------------------------------------------- |
| GET    | `/templates`               | Lista todas las plantillas.                                                      |
| POST   | `/templates`               | Crea una plantilla. Su dirección `from` debe pertenecer a un dominio verificado. |
| GET    | `/templates/:id`           | Obtiene una plantilla.                                                           |
| PATCH  | `/templates/:id`           | Edita una plantilla.                                                             |
| DELETE | `/templates/:id`           | Elimina una plantilla.                                                           |
| POST   | `/templates/:id/duplicate` | Copia una plantilla — devuelve el ID de la nueva plantilla.                      |
| GET    | `/templates/:id/usage`     | Qué campañas y pasos de workflow dependen de esta plantilla.                     |

### Campañas

| Método | Ruta                       | Descripción                                                                               |
| ------ | -------------------------- | ----------------------------------------------------------------------------------------- |
| GET    | `/campaigns`               | Lista todas las campañas.                                                                 |
| POST   | `/campaigns`               | Crea una campaña en `DRAFT`. Su dirección `from` debe pertenecer a un dominio verificado. |
| GET    | `/campaigns/:id`           | Obtiene una campaña.                                                                      |
| PUT    | `/campaigns/:id`           | Reemplaza el contenido de una campaña.                                                    |
| DELETE | `/campaigns/:id`           | Elimina una campaña — 409 si aún hay ejecuciones activas.                                 |
| POST   | `/campaigns/:id/duplicate` | Copia una campaña — recibes de vuelta la nueva en `DRAFT`.                                |
| POST   | `/campaigns/:id/send`      | Envía la campaña ahora, o más tarde vía `scheduledFor`.                                   |
| POST   | `/campaigns/:id/cancel`    | Detiene una campaña que está `SCHEDULED` o `SENDING`.                                     |
| POST   | `/campaigns/:id/test`      | Envía una prueba a una dirección (`{ email: "you@example.com" }`).                        |
| GET    | `/campaigns/:id/stats`     | Recuentos actuales de envíos / aperturas / clics / rebotes.                               |

### Segmentos

| Método | Ruta                     | Descripción                                                                                                             |
| ------ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| GET    | `/segments`              | Lista todos los segmentos (sin paginar — la lista se mantiene pequeña).                                                 |
| POST   | `/segments`              | Crea un segmento. `type: "DYNAMIC"` requiere `condition`; `type: "STATIC"` la rechaza.                                  |
| GET    | `/segments/:id`          | Obtiene un segmento, con el `memberCount` en caché incluido.                                                            |
| PATCH  | `/segments/:id`          | Edita el nombre, la descripción, la condición (solo dinámicos) o `trackMembership`.                                     |
| DELETE | `/segments/:id`          | Elimina un segmento — 409 si una campaña activa depende de él.                                                          |
| GET    | `/segments/:id/contacts` | Listado de miembros paginado por página vía `page` y `pageSize` (máx. 100). Se evalúa en vivo para segmentos dinámicos. |
| POST   | `/segments/:id/members`  | Añade correos a un segmento **estático**. Body: `{ emails, createMissing?, subscribed? }`.                              |
| DELETE | `/segments/:id/members`  | Quita correos de un segmento **estático**. Body: `{ emails }`.                                                          |
| POST   | `/segments/:id/compute`  | Recalcula la membresía de un segmento dinámico monitoreado, emitiendo eventos de entrada/salida.                        |
| POST   | `/segments/:id/refresh`  | Actualización ligera del recuento — no se emiten eventos, no se escribe membresía.                                      |

### Workflows

Un workflow se guarda como un registro de **workflow** más un grafo de **pasos**, las **transiciones** que los conectan, y una **ejecución** por cada contacto que pasa por él. Los endpoints siguen esa misma estructura.

| Método           | Ruta                                       | Descripción                                                                                                        |
| ---------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| GET              | `/workflows`                               | Lista todos los workflows.                                                                                         |
| GET              | `/workflows/fields`                        | Campos de contacto y de evento utilizables dentro de los filtros de pasos `CONDITION`.                             |
| POST             | `/workflows`                               | Crea un workflow. Siempre empieza con `triggerType: EVENT` y `enabled: false`.                                     |
| GET              | `/workflows/:id`                           | Obtiene un workflow junto con sus pasos y transiciones.                                                            |
| PATCH            | `/workflows/:id`                           | Edita metadatos, el tipo/config del disparador, `enabled` o `allowReentry`.                                        |
| DELETE           | `/workflows/:id`                           | Elimina un workflow — solo cuando no queden ejecuciones activas (cancélalas o deja que terminen).                  |
| **Pasos**        |                                            |                                                                                                                    |
| POST             | `/workflows/:id/steps`                     | Añade un paso (`SEND_EMAIL`, `DELAY`, `WAIT_FOR_EVENT`, `CONDITION`, `WEBHOOK`, `UPDATE_CONTACT`, `EXIT`).         |
| PATCH            | `/workflows/:id/steps/:stepId`             | Edita la configuración de un paso.                                                                                 |
| DELETE           | `/workflows/:id/steps/:stepId?splice=true` | Elimina un paso; con `splice=true` las transiciones circundantes se reconectan automáticamente.                    |
| **Transiciones** |                                            |                                                                                                                    |
| POST             | `/workflows/:id/transitions`               | Conecta dos pasos. En pasos `CONDITION`, especifica `branch: "yes" \| "no"`.                                       |
| DELETE           | `/workflows/:id/transitions/:transitionId` | Elimina una transición.                                                                                            |
| **Ejecuciones**  |                                            |                                                                                                                    |
| POST             | `/workflows/:id/executions`                | Inicia una ejecución para un contacto manualmente. Acepta un `context` JSON opcional como variables por ejecución. |
| GET              | `/workflows/:id/executions`                | Lista ejecuciones; filtra por `status`.                                                                            |
| GET              | `/workflows/:id/executions/:executionId`   | Obtiene una ejecución.                                                                                             |
| DELETE           | `/workflows/:id/executions/:executionId`   | Cancela una ejecución que esté en curso o en espera.                                                               |
| POST             | `/workflows/:id/executions/cancel-all`     | Cancela todas las ejecuciones activas de una sola vez.                                                             |

### Eventos

| Método | Ruta                         | Descripción                                                                                                                                     |
| ------ | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| POST   | `/events/track`              | Endpoint separado del panel con `name`, `contactId` y `emailId`; no utiliza el formato ni el contrato de idempotencia y límites de `/v1/track`. |
| GET    | `/events`                    | Eventos recientes registrados en todo el proyecto.                                                                                              |
| GET    | `/events/stats`              | Estadísticas de eventos, agregadas.                                                                                                             |
| GET    | `/events/contact/:contactId` | El historial completo de eventos de un contacto.                                                                                                |
| GET    | `/events/names`              | Todos los nombres de evento distintos que el proyecto ha registrado.                                                                            |
| GET    | `/events/:eventName/usage`   | Dónde se usa un nombre de evento (filtros de segmento, disparadores de workflow, condiciones).                                                  |
| DELETE | `/events/:eventName`         | Purga todos los eventos con ese nombre del proyecto.                                                                                            |

### Dominios

| Método | Ruta                          | Descripción                                                                                               |
| ------ | ----------------------------- | --------------------------------------------------------------------------------------------------------- |
| GET    | `/domains/project/:projectId` | Dominios adjuntos a un proyecto, tanto verificados como pendientes.                                       |
| POST   | `/domains`                    | Registra un dominio para verificación y recibe los registros DNS a publicar.                              |
| GET    | `/domains/:id/verify`         | Ejecuta una verificación de inmediato (la comprobación en segundo plano igualmente corre cada 5 minutos). |
| DELETE | `/domains/:id`                | Desvincula un dominio.                                                                                    |

### Actividad y analíticas

| Método | Ruta                        | Descripción                                                                                                   |
| ------ | --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| GET    | `/activity`                 | Feed de actividad que abarca todos los recursos (envíos, aperturas, clics, rebotes, quejas, entrantes, etc.). |
| GET    | `/activity/stats`           | Recuentos agregados que alimentan los gráficos del panel.                                                     |
| GET    | `/activity/recent-count`    | Recuento de eventos recientes, usado por el indicador "en vivo" del panel.                                    |
| GET    | `/activity/types`           | Los tipos de actividad presentes en el proyecto.                                                              |
| GET    | `/activity/upcoming`        | Envíos y ejecuciones programados que se acercan.                                                              |
| GET    | `/analytics/timeseries`     | Recuentos de envíos / aperturas / clics a lo largo del tiempo.                                                |
| GET    | `/analytics/top-campaigns`  | Campañas con mejor rendimiento, clasificadas por métrica.                                                     |
| GET    | `/analytics/campaign-stats` | Desglose por campaña.                                                                                         |
| GET    | `/analytics/top-events`     | Nombres de eventos personalizados clasificados por frecuencia.                                                |

### Subidas

| Método | Ruta             | Descripción                                                                                                |
| ------ | ---------------- | ---------------------------------------------------------------------------------------------------------- |
| POST   | `/uploads/image` | Sube una imagen (multipart) para incrustarla en el cuerpo de las plantillas. Responde con una URL pública. |

### Autenticación y gestión de usuarios

Estas rutas dan soporte al panel, que inicia sesión con cookies JWT. Rara vez importan para el trabajo servidor a servidor, pero se incluye la lista para que nada quede sin documentar.

| Método | Ruta                                          | Descripción                                                           |
| ------ | --------------------------------------------- | --------------------------------------------------------------------- |
| POST   | `/auth/login`                                 | Inicia sesión con correo y contraseña; establece una cookie JWT.      |
| POST   | `/auth/signup`                                | Registra un usuario (respeta `DISABLE_SIGNUPS`).                      |
| GET    | `/auth/logout`                                | Elimina la cookie de sesión.                                          |
| GET    | `/auth/oauth-config`                          | Los proveedores OAuth configurados actualmente.                       |
| POST   | `/auth/verify-email`                          | Confirma un correo usando el token del enlace enviado por correo.     |
| POST   | `/auth/request-verification`                  | Vuelve a enviar el correo de verificación.                            |
| POST   | `/auth/request-password-reset`                | Envía por correo un enlace de restablecimiento de contraseña.         |
| POST   | `/auth/reset-password`                        | Establece una nueva contraseña usando un token.                       |
| GET    | `/users/@me`                                  | El usuario con la sesión iniciada actualmente.                        |
| GET    | `/users/@me/projects`                         | Proyectos pertenecientes al usuario.                                  |
| POST   | `/users/@me/projects`                         | Crea un proyecto.                                                     |
| PATCH  | `/users/@me/projects/:id`                     | Edita la configuración del proyecto.                                  |
| POST   | `/users/@me/projects/:id/checkout`            | Inicia una sesión de Stripe Checkout.                                 |
| POST   | `/users/@me/projects/:id/billing-portal`      | Abre el portal de facturación de Stripe.                              |
| GET    | `/users/@me/projects/:id/billing-limits`      | Lee los límites de facturación por categoría.                         |
| PUT    | `/users/@me/projects/:id/billing-limits`      | Cambia los límites de facturación por categoría.                      |
| GET    | `/users/@me/projects/:id/billing-consumption` | Consumo en el período de facturación actual.                          |
| GET    | `/users/@me/projects/:id/billing-invoices`    | Las facturas de Stripe del proyecto.                                  |
| GET    | `/users/@me/projects/:id/security`            | Resumen de seguridad — tasas de rebote/queja, suspensiones recientes. |
| POST   | `/users/@me/projects/:id/reset`               | Borra todos los datos del proyecto (no se puede deshacer).            |
| DELETE | `/users/@me/projects/:id`                     | Elimina el proyecto completo.                                         |
| GET    | `/projects/:id/setup-state`                   | Estado de progreso de la incorporación.                               |
| GET    | `/projects/:id/security`                      | Estado de seguridad de un proyecto.                                   |
| GET    | `/projects/:id/members`                       | Los miembros del equipo del proyecto.                                 |
| POST   | `/projects/:id/members`                       | Invita a alguien al equipo.                                           |
| PATCH  | `/projects/:id/members/:userId`               | Actualiza el rol de un miembro.                                       |
| DELETE | `/projects/:id/members/:userId`               | Elimina a alguien del equipo.                                         |

### Claves de API

Claves secretas con alcance, por proyecto. Consulta la [guía de claves de API](/es/guides/api-keys) para ver el catálogo de permisos y las reglas de creación. Las tres requieren una sesión de panel con inicio de sesión y el permiso `apikeys:manage` — ninguna puede llamarse con una clave de API.

| Método | Ruta                  | Descripción                                                                                   |
| ------ | --------------------- | --------------------------------------------------------------------------------------------- |
| POST   | `/apikeys`            | Crea una clave secreta con alcance. Devuelve su valor en texto plano una vez, nunca más.      |
| GET    | `/apikeys`            | Lista las claves del proyecto (nombre, prefijo, alcances, marcas de tiempo — nunca la clave). |
| POST   | `/apikeys/:id/revoke` | Revoca una clave de inmediato; el resto sigue funcionando.                                    |

### Configuración

| Método | Ruta      | Descripción                                                                                                                          |
| ------ | --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| GET    | `/config` | Endpoint de feature-flags sin autenticación — reporta qué integraciones están activas (proveedores OAuth, facturación, S3, SMTP, …). |

### Endpoints internos de webhook

La infraestructura de correo y facturación entrega aquí sus eventos. Tus aplicaciones nunca llaman a estos — aparecen solo para que la lista esté completa.

| Método | Ruta                        |
| ------ | --------------------------- |
| POST   | `/webhooks/sns`             |
| POST   | `/webhooks/incoming/stripe` |

## Bibliotecas cliente

### Node.js

```javascript
const BITELIO_SECRET_KEY = process.env.BITELIO_SECRET_KEY;

async function sendEmail(to, subject, body) {
  const response = await fetch('https://api.bitelio.com/v1/send', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${BITELIO_SECRET_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ to, subject, body })
  });

  const data = await response.json();

  if (!data.success) {
    throw new Error(`[${data.error.code}] ${data.error.message}`);
  }

  return data.data;
}
```

### Python

```python
import os
import requests

BITELIO_SECRET_KEY = os.environ['BITELIO_SECRET_KEY']

def send_email(to, subject, body):
    response = requests.post(
        'https://api.bitelio.com/v1/send',
        headers={
            'Authorization': f'Bearer {BITELIO_SECRET_KEY}',
            'Content-Type': 'application/json'
        },
        json={'to': to, 'subject': subject, 'body': body}
    )

    data = response.json()

    if not data.get('success'):
        error = data['error']
        raise Exception(f"[{error['code']}] {error['message']}")

    return data['data']
```

### cURL

```bash
curl -X POST https://api.bitelio.com/v1/send \
  -H "Authorization: Bearer $BITELIO_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "user@example.com", "subject": "Hello", "body": "Message"}'
```

## Qué sigue

* [Enviar un correo transaccional](/es/api-reference/public-api/sendEmail)
* [Registrar un evento](/es/api-reference/public-api/trackEvent)
* [Referencia de códigos de error](/es/api-reference/errors)
