---
title: "Create an async CSV import job"
method: POST
path: "/email_blocks/import"
tags: ["Email Suppression Imports"]
---

# Create an async CSV import job

`POST /email_blocks/import`

## Request

- Base URL: `https://api.telnyx.com/v2`
- URL: `https://api.telnyx.com/v2/email_blocks/import`
- Auth: HTTP bearer

Accepts `multipart/form-data` with a `file` field (the CSV) and an
optional `block_ttl_days` (integer >0, default 30). Validates:
  - content ≤ 25 MiB, else `413`
  - row count ≤ 250 000, else `413`
  - header-only / all-blank / undetectable provider → `400`
Returns `202` with the import record (status `pending`); an Oban
worker (`EmailBlockImportWorker`, max_attempts 3) transitions
`pending → processing → completed | failed`.

Native Telnyx exports are detected by the stable first-12-column
header signature (`id` … `group_id`) and are restored with their
original `from`, `domain_id`, `group_id`, `source`, `status`,
`expires_at`, plus `bounce_category`, `dsn_code`, and `meta` when
present (`scope` is re-derived from `domain_id`/`from`; the
exported `scope` cell must be a valid enum value). Lifecycle
changes reconcile through the same create path as the API: a row
already in the requested state restores its mutable backup fields
without a new audit event, and a real transition (e.g. tombstone
→ active) appends the matching lifecycle event. `block_ttl_days`
is not applied to native rows — their exported `expires_at` is
preserved verbatim.

Competitor and generic imports (SendGrid / Mailgun / SES /
generic) remain account-scoped (`from`, `domain_id`, `group_id`,
`scope` are not read) and `block_ttl_days` applies only to
imported `manual_block` rows; other reasons get `expires_at: nil`.
Provider is auto-detected from the CSV header
(`sendgrid` / `mailgun` / `ses` / `generic`).

## Request body `multipart/form-data`

- CreateImportRequest
  - `file` string, binary, required: The CSV file (Plug.Upload). Missing/non-upload → 400.
  - `block_ttl_days` integer: TTL for imported `manual_block` rows; other reasons get `expires_at: null`. Invalid/missing → falls back to 30.

## Response `202`

Import job accepted (status `pending`).

- EmailBlockImportResponse
  - `data` EmailBlockImport, required: Import job. Schema fields hidden: `account_id`, `csv_content`, `block_ttl_days`. Nullable fields use the omit-nullable pattern.
    - `id` string, uuid, required
    - `record_type` 'email_block_import', required: View-only.
    - `status` 'pending' | 'processing' | 'completed' | 'failed', required
    - `total` integer, required: Data-row count at upload.
    - `provider` 'sendgrid' | 'mailgun' | 'ses' | 'generic': Omitted when nil.
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `completed_at` string, date-time: Omitted until terminal success.
    - `processed_rows` integer: Only when `status == completed`.
    - `created_count` integer: Only when `status == completed`.
    - `existing_count` integer: Only when `status == completed`.
    - `skipped_count` integer: Only when `status == completed`.
    - `error_count` integer: Rows that passed CSV parsing but failed suppression creation. This is the creation-failure subset of `skipped_count`; parser-rejected rows equal `skipped_count - error_count`. Only when `status == completed`.
    - `errors` object: `{row_number: reason}`; only rendered when non-empty.
    - `failure_reason` string: Only on terminal failure.

## Other responses

- `400`: Query-param validation error (`source.pointer /<field>`).
- `401`: Missing or invalid gateway auth.
- `406`: Framework-rendered error (e.g. 406 Not Acceptable, 405 Method Not Allowed, 415 Unsupported Media Type). HTTP status matches the error and the body `code` carries that same status (e.g. `"406"`, not a hardcoded `"500"`). The explicit `500.json` clause still emits `code: "500"` for genuine 500s.
- `413`: Two distinct 413 paths. Content-level cap (decoded CSV > 25 MiB or > 250 000 rows) returns `10015` with `source.pointer /file`. The multipart parser raises `RequestTooLargeError` when the encoded body exceeds 26 MiB — that's a framework error rendered with body `code: "413"` (matches the HTTP status).
- `422`: Validation error (changeset or internal `Params`). One error object per field, `source.pointer /data/attributes/<field>`.
- `500`: Import-create fallback (`code 10019`, "Failed to create import").

## Changes

- **2026-10-15** `a72569d69430`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/team-telnyx/apis/telnyx-api/changes/email_blocks/import/post.md)

---

[API](https://skmtc.dev/team-telnyx/apis/telnyx-api.md) - [All operations](https://skmtc.dev/team-telnyx/apis/telnyx-api/llms.txt) - [OpenAPI document](https://skmtc.dev/team-telnyx/apis/telnyx-api/revisions/0f17b8167873?raw)
