# Export & Import

> Bulk CSV export and import for contacts and tags

Source: https://docs.soosh.io/reference/api/export-import/

## Overview

The export/import endpoints back the CSV **Export** and **Import** actions in the Contacts screen. They are generic: a small set of server-defined tables are exportable and importable, each with an allow-listed set of columns. Only two resources are supported today:

| Table | Export permission | Import permission |
|-------|-------------------|-------------------|
| `contacts` | `contacts:export` | `contacts:import` |
| `tags` | `tags:export` | `tags:import` |

> **Careful:** `tags:export` and `tags:import` are checked by the handlers but are **not part of the seeded
> permission catalogue** — no role can be granted them. In practice only super admins (who bypass
> permission checks) can export or import the `tags` table today.

> **Note:** Columns are restricted to a server-controlled allow-list. Requesting a column outside that list returns `400`. Use the config endpoints below to discover the valid columns for a table.

## Export Data

Export rows of a table as a CSV file. The response is a CSV **file download** (not a JSON envelope), with `Content-Type: text/csv` and a `Content-Disposition: attachment` header naming the file `<table>_export_<timestamp>.csv`.

`POST /api/export`

### Request Body

```json
{
  "table": "contacts",
  "columns": ["phone_number", "profile_name", "tags"],
  "filters": {
    "search": "john",
    "tags": "vip,lead"
  },
  "format": "csv"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `table` | string | Yes | Table to export: `contacts` or `tags`. |
| `columns` | array | No | Columns to include. Must be a subset of the table's allowed columns. Defaults to the table's default columns when omitted or empty. |
| `filters` | object | No | Map of filter name → value (see below). |
| `format` | string | No | Output format. Only `csv` is produced. |

#### Filters

| Filter | Applies to | Description |
|--------|-----------|-------------|
| `search` | contacts, tags | Case-insensitive match. Contacts match on phone number or name; tags match on name or description. |
| `tags` | contacts | Comma-separated tag list; returns contacts having any of the tags. |

#### Contact columns

| Column | Default | Label |
|--------|---------|-------|
| `phone_number` | Yes | Phone Number |
| `profile_name` | Yes | Name |
| `tags` | Yes | Tags |
| `whats_app_account` | No | WhatsApp Account |
| `assigned_user_id` | No | Assigned User ID |
| `last_message_at` | No | Last Message At |
| `created_at` | No | Created At |
| `updated_at` | No | Updated At |

#### Tag columns

| Column | Default | Label |
|--------|---------|-------|
| `name` | Yes | Name |
| `color` | Yes | Color |
| `description` | Yes | Description |
| `created_at` | No | Created At |

> **Note:** When phone-number masking is enabled for the organization, contact phone numbers and phone-like names are masked in the exported CSV. Tags are joined with commas, and timestamps use RFC 3339.

### Response

A CSV payload. The first row is the header (using the human labels above), followed by one row per record:

```
Phone Number,Name,Tags
14155550123,John Doe,"vip,lead"
```

## Import Data

Import rows from a CSV file via `multipart/form-data`. Duplicates are detected on the table's unique column.

`POST /api/import`

### Form Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `table` | text | Yes | Table to import into: `contacts` or `tags`. |
| `file` | file | Yes | The CSV file (max 10 MB, max 10,000 data rows). |
| `column_mapping` | text (JSON) | No | JSON object mapping a CSV header → target column, e.g. `{"Mobile":"phone_number"}`. Unmapped headers are matched by (lower-cased) name or label. |
| `update_on_duplicate` | text | No | `"true"` to update existing rows on a unique-column match; otherwise duplicates are skipped. |

#### Required & optional columns

| Table | Required | Optional | Unique column |
|-------|----------|----------|---------------|
| `contacts` | `phone_number` | `profile_name`, `whats_app_account`, `tags`, `assigned_user_id` | `phone_number` |
| `tags` | `name` | `color`, `description` | `name` |

> **Note:** On import, a leading `+` is stripped from phone numbers, `tags` is a comma-separated list, and `assigned_user_id` must be a valid user UUID. Rows missing a required column, or exceeding limits, are counted as errors and reported per-row.

### Response

```json
{
  "status": "success",
  "data": {
    "created": 42,
    "updated": 3,
    "skipped": 5,
    "errors": 1,
    "messages": [
      "Row 12: phone_number - phone number is required"
    ]
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `created` | integer | New rows inserted. |
| `updated` | integer | Existing rows updated (only when `update_on_duplicate` is true). |
| `skipped` | integer | Duplicate rows left unchanged. |
| `errors` | integer | Rows that failed validation or insertion. |
| `messages` | array | Human-readable per-row error messages. |

## Get Export Config

Describe the exportable columns for a table — used by the UI to build the column picker.

`GET /api/export/{table}/config`

Requires the table's export permission (e.g. `contacts:export`).

### Response

```json
{
  "status": "success",
  "data": {
    "table": "contacts",
    "columns": [
      { "key": "phone_number", "label": "Phone Number" },
      { "key": "profile_name", "label": "Name" },
      { "key": "tags", "label": "Tags" }
    ],
    "default_columns": ["phone_number", "profile_name", "tags"]
  }
}
```

## Get Import Config

Describe the importable columns for a table — used by the UI to build the column-mapping step.

`GET /api/import/{table}/config`

Requires the table's import permission (e.g. `contacts:import`).

### Response

```json
{
  "status": "success",
  "data": {
    "table": "contacts",
    "required_columns": [
      { "key": "phone_number", "label": "Phone Number" }
    ],
    "optional_columns": [
      { "key": "profile_name", "label": "Name" },
      { "key": "whats_app_account", "label": "WhatsApp Account" },
      { "key": "tags", "label": "Tags" },
      { "key": "assigned_user_id", "label": "Assigned User ID" }
    ],
    "unique_column": "phone_number"
  }
}
```

## See Also

- [Contacts API](https://docs.soosh.io/reference/api/contacts) - Manage contacts individually
- [Roles](https://docs.soosh.io/reference/api/roles) - `import`/`export` permission actions
