# Importar contactos desde CSV (/es/guides/importing-contacts)

Cuando transmitir contactos uno por uno mediante `/v1/track` no es práctico — migrar desde otra plataforma, poblar una lista nueva, sincronizar un lote grande — Bitelio te permite importarlos en bloque desde un archivo CSV.

## Formato CSV

Tu archivo necesita una fila de encabezado, y `email` es la única columna que debe estar presente. Cualquier columna adicional se guarda en el contacto como campo personalizado en `data.<columnName>`.

Un archivo mínimo:

```csv
email
ada@example.com
grace@example.com
linus@example.com
```

Un archivo más completo con campos personalizados:

```csv
email,firstName,plan,signupDate
ada@example.com,Ada,pro,2026-01-15T00:00:00Z
grace@example.com,Grace,enterprise,2025-11-02T00:00:00Z
linus@example.com,Linus,free,2026-04-21T00:00:00Z
```

Con este archivo, cada contacto importado obtiene `data.firstName`, `data.plan` y `data.signupDate`.

### Reglas y límites

* **Tamaño de archivo**: 5 MB máximo por carga. Las listas más grandes deben dividirse en varias importaciones.
* **Codificación**: UTF-8. Los archivos en otras codificaciones corren el riesgo de que los valores de campos personalizados salgan corruptos.
* **Columna de email**: obligatoria y validada. Cualquier fila cuyo email esté ausente o mal formado vuelve en el reporte de errores.
* **Nombres de columna reservados**: `id`, `subscribed`, `createdAt`, `updatedAt` y las variables de URL autogeneradas (`unsubscribeUrl`, etc.) se descartan silenciosamente — déjalas fuera de tu archivo.
* **Columnas de fecha**: dales formato ISO 8601 (`2026-05-06T12:00:00Z`) para que Bitelio las tipe como fechas, desbloqueando los operadores de segmento `within` / `olderThan`.
* **Columnas booleanas**: los valores `true`, `false`, `yes`, `no` (en cualquier capitalización) se convierten en booleanos y aparecen como un interruptor booleano en los filtros de segmento.
* **Columnas numéricas**: enteros y decimales simples (`42`, `3.14`) se convierten en números, usables con los operadores de segmento `gt` / `lt`. Los valores con ceros a la izquierda (`01234`), un prefijo `+`, o notación científica se mantienen como texto — esto protege a los ID, códigos postales y números de teléfono de corromperse.
* **Contactos existentes**: cuando el email de una fila ya pertenece a un contacto, ese contacto se **actualiza** — las columnas del CSV se fusionan en su `data`. No se crea un duplicado y el resto del registro queda intacto.

## Importar tu CSV

import {Tab, Tabs} from 'fumadocs-ui/components/tabs';

<Tabs items={['Panel', 'API']}>
  <Tab value="Panel">
    1. Ve a **Contactos** y pulsa **Importar**.
    2. Selecciona el CSV. Bitelio comprueba el encabezado y te muestra una vista previa de las primeras filas.
    3. Confirma — la importación se pone en cola y se procesa en segundo plano.
    4. Una vez terminada, la página de Importaciones muestra el resultado, incluyendo cualquier error a nivel de fila.
  </Tab>

  <Tab value="API">
    Para automatización, usa la API de importación:

    **Paso 1 — subir el CSV**

    ```bash
    curl -X POST https://api.bitelio.com/contacts/import \
      -H "Authorization: Bearer sk_..." \
      -F "file=@contacts.csv"
    ```

    La respuesta incluye un `jobId`:

    ```json
    { "jobId": "imp_abc123", "status": "queued" }
    ```

    **Paso 2 — consultar el estado**

    ```bash
    curl https://api.bitelio.com/contacts/import/imp_abc123 \
      -H "Authorization: Bearer sk_..."
    ```

    Sigue consultando a un intervalo de pocos segundos hasta que `status` marque `completed` o `failed`. La respuesta final trae los recuentos:

    ```json
    {
      "jobId": "imp_abc123",
      "status": "completed",
      "totalRows": 12000,
      "imported": 11985,
      "updated": 8,
      "skipped": 0,
      "errors": [
        { "row": 47,   "email": "bad@",      "reason": "invalid_email" },
        { "row": 1042, "email": "@example",  "reason": "invalid_email" }
      ]
    }
    ```

    `imported` es el número de contactos creados desde cero, mientras que `updated` cuenta los existentes cuyo `data` fue parcheado. Cada entrada en `errors` te da el número de fila y el motivo, así que las filas problemáticas pueden corregirse y volver a subirse.
  </Tab>
</Tabs>

## Consejos

* **Contactos sin suscripción**: todo contacto importado empieza suscrito. Si el CSV contiene personas que nunca aceptaron participar, configura un flujo de trabajo que filtre a quien no debas contactar, o importa primero y luego ejecuta `POST /contacts/bulk-unsubscribe` para que queden en el registro sin ser enviables.
* **Los campos personalizados aparecen en segmentos de inmediato**: en el momento en que termina la importación, los segmentos dinámicos pueden filtrar sobre cualquiera de las columnas importadas.
* **Reimportaciones idempotentes**: volver a subir el mismo CSV actualiza los contactos en su lugar en vez de duplicarlos — así que, tras corregir una columna, una nueva subida empuja el valor corregido a cada fila coincidente.

## Relacionado

* [Campos personalizados](/es/guides/custom-fields) — cómo se tipan y se usan los campos `data.<column>` que importas.
* [Operaciones masivas](/es/api-reference/overview#contacts) — `bulk-subscribe`, `bulk-unsubscribe`, `bulk-delete` para acciones masivas sobre contactos existentes (máximo 1000 por llamada).
