Preview a CSV import

View as Markdown
Step 1 of the CSV import. Upload the file as `multipart/form-data` (field `file`). The response has the headers, the first 10 rows, a proposed column mapping, validation stats and the file's SHA-256 `fileHash`. Nothing is stored. Review or adjust the mapping, then send the **same file** with it to `POST /api/campaigns/{campaignId}/contacts/upload-confirm`. **File rules.** The name must end in `.csv` and the part's MIME type must be `text/csv`, `application/csv` or `text/plain`. Maximum 10 MB, comma-separated, first row = headers. Encoding UTF-8 (a BOM is fine) or UTF-16 with BOM; other encodings are refused, so use "CSV UTF-8" when saving from Excel. XLSX, XLS, PDF and other binary files are refused. Each field is limited to 1 MB and each row to 500 columns. Blank lines are ignored. **Detected mapping.** Headers are matched case-insensitively. Columns containing `whatsapp`/`wa` map to `whatsappNumber`, `phone`/`tel`/`mobile`/`celular`/`telefono`/`numero` to `phoneNumber`, `email`/`correo`/`mail` to `email`, and `name`/`nombre`/`contact` to `name`. Otherwise the sample values decide (mostly phone-shaped, or mostly containing `@`). Each field is claimed by the **first** matching column; later matches and everything else become `metadata`. **Stats** cover only the first 1,000 rows. A phone is valid there with 7 to 15 digits after removing spaces, dashes and parentheses; confirm applies the stricter rules described on that endpoint. `errors` holds up to 100 entries. Preview cells starting with `=`, `+`, `-` or `@` are prefixed with `'` so they cannot run as spreadsheet formulas; stored values are not changed. The campaign itself is not looked up here: an unknown `campaignId` still gets a preview. **Idempotency.** Safe to retry: nothing is written, and the same file always returns the same `fileHash`. **Access** - **Required scope:** `write` (or `full`). - **Rate limit:** Contacts import — 5 requests/min per workspace, shared by every route under `/api/campaigns/{campaignId}/contacts`, on top of the general API limit. See [Rate limits](/rate-limits). - **Plan:** Refused with 403 `BILLING_ERROR` when the workspace contact cap is already reached (Starter 500, Growth 2,000, Business 20,000).

Authentication

AuthorizationBearer
Workspace API key: `jl_` followed by 64 lowercase hex characters, created by the workspace owner in the dashboard (**Settings → API Keys**) and sent as `Authorization: Bearer jl_...`. The plaintext is shown once, at creation; Jelliu stores only a SHA-256 hash. A workspace can hold up to 25 active keys. | Scope | GET / HEAD | POST / PUT / PATCH / DELETE | Admin-only routes | | --- | --- | --- | --- | | `read` | Yes | No | No | | `write` | Yes | Yes | No | | `full` | Yes | Yes | Yes | Operations restricted to admins or owners reject keys without the `full` scope with `403`, and say so in their description. No key, whatever its scope, can mint or revoke API keys or rotate a webhook secret — that requires a signed-in owner session. A revoked key stops authenticating within about 10 seconds. See [Authentication](/authentication).

Path parameters

campaignIdstringRequiredformat: "uuid"

UUID of the campaign the file is meant for. Only its format is checked here (400 if not a UUID); it is not looked up.

Request

This endpoint expects a multipart form containing a file.
filefileRequired

The CSV file (form field file, .csv, max 10 MB).

Response

Parsed preview.
successtrue

Always true on 200.

dataobject

Errors

400
Bad Request Error
401
Unauthorized Error
403
Forbidden Error
429
Too Many Requests Error