# Contactos (/es/concepts/contacts)

Un contacto es el registro que Bitelio guarda de un único destinatario de correo: un identificador único ligado a una dirección de correo.

## Añadir contactos

Hay varias formas de incorporar contactos a un proyecto de Bitelio:

* Vía [`/v1/track`](/es/api-reference/public-api/trackEvent) — si registras un evento contra un correo que aún no está en el proyecto, el contacto se crea al vuelo (y empieza suscrito).
* Vía [`POST /contacts`](/es/api-reference/contacts/createContact) — crea o hace upsert de un contacto. Si el correo ya existe, se **actualiza** en lugar de rechazarse; la respuesta trae un bloque `_meta: { isNew, isUpdate }`, con estado `201` cuando el contacto se creó y `200` cuando se actualizó.
* **Importación CSV** masiva mediante `POST /contacts/import` — envía un CSV (≤ 5 MB) y luego sigue el job con `GET /contacts/import/:jobId`. `email` debe ser la primera columna; cada columna restante se convierte en una clave bajo `data.*`.
* **Suscripción/baja/eliminación** masiva mediante `POST /contacts/bulk-subscribe`, `bulk-unsubscribe` y `bulk-delete` — cada una toma como máximo 1000 IDs de contacto y devuelve un ID de job que puedes consultar en `GET /contacts/bulk/:jobId`.
* Añadiendo correos a un **segmento estático** con `POST /segments/:id/members` y `createMissing: true`, lo que crea cualquier dirección que aún no esté en el proyecto.
* A mano, desde el panel.

## Datos del contacto

Se pueden adjuntar pares clave-valor arbitrarios a cualquier contacto, y esos datos alimentan luego la segmentación y la personalización de plantillas.

### Tipos de datos

Para cada clave de un proyecto, Bitelio infiere el tipo a partir del **primer valor no nulo** que encuentra:

| Tipo    | Descripción                                                                                                                                            |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| String  | Texto que no se interpreta como fecha.                                                                                                                 |
| Number  | Enteros y decimales por igual.                                                                                                                         |
| Boolean | `true` o `false`.                                                                                                                                      |
| Date    | Cadenas con formato `YYYY-MM-DD` o `YYYY-MM-DDTHH:MM:SS[.sss]Z`. Los operadores de segmento que entienden fechas requieren la forma ISO 8601 completa. |

La inferencia de tipos ocurre una sola vez por proyecto, en la primera escritura. Así que si una clave se tipa como `number`, los valores de texto posteriores se siguen guardando — pero la interfaz de filtros de segmento sigue tratando la clave como `number`. ¿Mezclaste los tipos por accidente? La forma más limpia de arreglarlo es `DELETE /contacts/fields/:field` para eliminar el campo por completo, y luego reimportarlo con valores consistentes.

<Callout title="Tipo de dato por defecto" type="info">
  Cualquier cosa que no se reconozca como número, booleano o fecha ISO 8601 se guarda como string por defecto.
</Callout>

### Valores no persistentes

Envolver un valor como `{ value, persistent: false }` lo hace disponible para un único renderizado sin llegar a escribirlo nunca en el contacto:

```json
{
  "email": "user@example.com",
  "data": {
    "firstName": "Ada",
    "resetCode": { "value": "ABC123", "persistent": false }
  }
}
```

Aquí `firstName` se guarda en el contacto, mientras que `resetCode` existe solo para este envío y se descarta después. Eso lo convierte en la herramienta adecuada para secretos efímeros — códigos de restablecimiento de contraseña, enlaces mágicos, tokens de un solo uso.

### Tratamiento especial de valores

Unos pocos valores reciben un tratamiento especial cuando se crea o actualiza un contacto:

| Valor               | Comportamiento                             | Ejemplo                                    |
| ------------------- | ------------------------------------------ | ------------------------------------------ |
| Cadena vacía (`""`) | Se omite — no se guarda ni cambia nada     | `{ name: "" }` → El campo se omite         |
| `null`              | Elimina el campo de los datos del contacto | `{ name: null }` → El campo se elimina     |
| Otros valores       | Se escriben normalmente                    | `{ name: "John" }` → Se guarda como "John" |

<Callout title="Eliminar datos de un contacto" type="info">
  Pasa `null` en un campo al crear o actualizar un contacto para borrarlo. Una cadena vacía no hace lo mismo —
  las cadenas vacías se filtran antes de la escritura, así que los datos existentes sobreviven.
</Callout>

### Claves reservadas

Un puñado de claves pertenecen a Bitelio y viven en el propio contacto en lugar de en `data`. Son legibles, pero no se pueden escribir a través de `data`:

| Clave        | Descripción                                                      |
| ------------ | ---------------------------------------------------------------- |
| `email`      | La dirección de correo del contacto                              |
| `createdAt`  | Cuándo se creó el contacto                                       |
| `updatedAt`  | Cuándo se modificó el contacto por última vez                    |
| `subscribed` | Si el contacto recibe actualmente correo de marketing (booleano) |

Además, estas claves se **eliminan silenciosamente** de cualquier payload de `data` — incluirlas en `/v1/track`, `POST /contacts` o `PATCH /contacts/:id` ni las guarda ni produce un error:

`id`, `bitelio_id`, `bitelio_email`, `unsubscribeUrl`, `subscribeUrl`, `manageUrl`

Las tres últimas se generan de nuevo para cada destinatario en el momento del envío y aparecen como variables de plantilla (ver [Plantillas](/es/concepts/templates)).

### Claves especiales

| Clave  | Descripción                                                                                                                                                                                                              |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| locale | El idioma preferido del contacto como código ISO 639 (p. ej. 'en', 'fr', 'es'). Cuando está definido, tiene prioridad sobre el idioma general del proyecto en las páginas orientadas al contacto y en los pies de correo |

## Estado de suscripción

Cada contacto lleva un indicador `subscribed` que rige qué categorías de correo le llegan. Los contactos creados vía `/v1/track` empiezan suscritos; con `POST /contacts` el indicador toma lo que envíes (por defecto `false`).

En las actualizaciones, **omitir `subscribed` conserva lo que sea que tenga actualmente** — la omisión y `false` son cosas distintas. Envía un `true` o `false` explícito siempre que quieras un cambio.

Cada vez que `subscribed` cambia, Bitelio registra un evento en el contacto automáticamente:

* pasa a `true` → un evento `contact.subscribed`
* pasa a `false` → un evento `contact.unsubscribed`

Esto ocurre sin importar qué causó el cambio — una edición en el panel, la página pública de cancelación, un job masivo, o una cancelación automática tras un rebote o una queja de spam. Los flujos de trabajo pueden ramificarse a partir de ambos eventos.

### Cómo un contacto termina sin suscripción

Hay varios caminos hacia el estado sin suscripción:

* **Manual** — alguien lo activa/desactiva en el panel o mediante la API.
* **Autoservicio** — el contacto hace clic en un enlace de cancelación en un correo, cayendo en la página pública de cancelación alojada.
* **Automático por rebote duro** — un rebote permanente da de baja al contacto; los rebotes suaves dejan el estado sin cambios.
* **Automático por queja** — el contacto reporta tu correo como spam y su proveedor de buzón traslada la queja a Bitelio.

### Correos según el estado de suscripción

Que un correo de marketing se entregue depende del indicador de suscripción; el correo transaccional siempre pasa, esté suscrito o no.

| Tipo de correo                                                             | Suscrito  | No suscrito  |
| -------------------------------------------------------------------------- | --------- | ------------ |
| **Transaccional** (vía [/v1/send](/es/api-reference/public-api/sendEmail)) | Entregado | Entregado    |
| **Campañas** (marketing)                                                   | Entregado | No entregado |
| **Campañas** (headless)                                                    | Entregado | No entregado |
| **Campañas** (transaccional)                                               | Entregado | Entregado    |
| **Automatizaciones** (plantilla marketing)                                 | Entregado | No entregado |
| **Automatizaciones** (plantilla headless)                                  | Entregado | No entregado |
| **Automatizaciones** (plantilla transaccional)                             | Entregado | Entregado    |

<Callout title="Correos transaccionales y plantillas de marketing" type="warn">
  Pasar por el endpoint transaccional (`/v1/send`) no permite que una plantilla de marketing llegue a un contacto sin suscripción —
  la comprobación de suscripción sigue aplicando. Si el mensaje tiene que llegar sin importar la baja, usa una plantilla transaccional.
</Callout>

## Qué sigue

<Cards>
  <Card title="Campos personalizados" href="/es/guides/custom-fields">
    Configurar, tipar y ordenar datos arbitrarios en los contactos.
  </Card>

  <Card title="Importar desde CSV" href="/es/guides/importing-contacts">
    Carga contactos y sus campos de forma masiva.
  </Card>

  <Card title="Segmentos" href="/es/concepts/segments">
    Organiza los contactos en grupos dinámicos o estáticos para segmentarlos.
  </Card>

  <Card title="Páginas de cancelación" href="/es/guides/unsubscribe-pages">
    Las páginas alojadas de cancelación y preferencias, y sus variables de URL.
  </Card>
</Cards>
