---
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`

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`. `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`).

## 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 — 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").

---

[API](https://skmtc.net/team-telnyx/apis/telnyx-api-2.md) · [All operations](https://skmtc.net/team-telnyx/apis/telnyx-api-2/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/team-telnyx/telnyx-api-2/versions/8f5f4e537994/schema)
