# Export and import data

> Export whole-team data as queued CSV files, download documents and quick CSVs, and import service pricing from a CSV file.

Web page: https://developer.yoprint.com/guides/imports-and-exports/

## Export whole-team data

Large exports, such as every sales order in a quarter, run in the background. You start one, wait for it to be ready, then download the file.

**Diagram:** Downloading before the export is ready answers 404. An export can also end as failed.

1. **Start it**: The answer has the export's `id`
2. **pending**: Waiting its turn
3. **generating**: YoPrint builds the file
4. **ready**: Download the file

1) See which exports exist, and the columns and options each takes:

   `GET /v1/api/store/{team_slug}/data_export/definitions` [List export types and their columns](https://developer.yoprint.com/reference/data-exports/v1-data-export-definitions.md)

2) Start an export. For example, sales orders:

   `POST /v1/api/store/{team_slug}/data_export/export_sales_order` [Export sales orders in a date range to CSV](https://developer.yoprint.com/reference/data-exports/v1-data-export-export-sales-order.md)

   ```json
   {
     "start_date": "2026-10-01T00:00:00Z",
     "end_date": "2026-12-31T23:59:59Z",
     "date_field": "issue_date",
     "idempotency_key": "so-export-2026-q4"
   }
   ```

   The answer is the export, with its `id` and `status`.

3) Check it until `status` is `ready`. It moves from `pending` to `generating`, then to `ready` or `failed`.

   `GET /v1/api/store/{team_slug}/data_export/{id}` [Get a data export](https://developer.yoprint.com/reference/data-exports/v1-data-export-show.md)

4) Download the file, with the export's `id`. The answer is the file itself, not JSON, so save it:

   `GET /v1/api/store/{team_slug}/data_export/{id}/download` [Download a data export's file](https://developer.yoprint.com/reference/data-exports/v1-data-export-download.md)

   ```bash
   curl -o sales-orders.csv \
     "https://secure.yoprint.com/v1/api/store/your-team/data_export/R38ZQ52Wx5meqn/download" \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Accept: application/json" \
     -H "X-Yoprint-Decimal-Format: exact-v1"
   ```

The other exports work the same way: customers, invoices, invoice items, payments, purchase orders, taxes, simple products and variant products.

`POST /v1/api/store/{team_slug}/data_export/export_customer` [Export all customers to CSV](https://developer.yoprint.com/reference/data-exports/v1-data-export-export-customer.md)

`POST /v1/api/store/{team_slug}/data_export/export_invoice` [Export invoices in a date range to CSV](https://developer.yoprint.com/reference/data-exports/v1-data-export-export-invoice.md)

`POST /v1/api/store/{team_slug}/data_export/export_payment_list` [Export payments and refunds in a date range to CSV](https://developer.yoprint.com/reference/data-exports/v1-data-export-export-payment-list.md)

- Exports over a date range need `start_date` and `end_date`, with the end on or after the start. The sales order export also needs `date_field`, the order date the range applies to: `issue_date`, `deposit_due`, `due_date`, `ship_due_date`, `cust_due_date` or `start_date`.
- `columns` picks the columns, from the export's definition.
- `idempotency_key`, up to 100 characters, makes the start safe to retry: the same key returns the same export instead of starting another. After a failure to queue, or an expired attempt, the same key queues it again. See [Retries and duplicates](https://developer.yoprint.com/concepts/retries-and-duplicates.md).
- Downloading before the export is ready answers `404`.

A team can run 3 exports at once, and one of each kind. See [Limits](https://developer.yoprint.com/concepts/limits.md). To list your team's exports:

`GET /v1/api/store/{team_slug}/data_export` [List data exports](https://developer.yoprint.com/reference/data-exports/v1-data-export-index.md)

## Quick CSV exports

Some lists come straight back as a CSV file, without queueing. They take a date range:

`POST /v1/api/store/{team_slug}/sales_order/export` [Export sales orders as CSV](https://developer.yoprint.com/reference/sales-orders/v1-sales-order-export.md)

`POST /v1/api/store/{team_slug}/purchase_order/export` [Export purchase orders by issue date](https://developer.yoprint.com/reference/purchase-orders/v1-purchase-order-export.md)

```json
{ "start_date": "2026-11-01T00:00:00Z", "end_date": "2026-11-30T23:59:59Z" }
```

They don't always answer with a file:

- With `"list_only": true`, the rows come back as JSON instead, and an empty range answers an empty `data` list.
- Without `list_only`, when nothing matches the date range, the answer is JSON `422` (`sales_order_export_error` or `purchase_order_export_error`), not an empty file.

Check the status and `Content-Type` before you save the body.

## Documents

PDFs and printable files for chosen records:

`POST /v1/api/store/{team_slug}/invoice/export_pdf` [Download invoices as one PDF](https://developer.yoprint.com/reference/invoices/v1-invoice-export-pdf.md)

`POST /v1/api/store/{team_slug}/job/export_job_detail` [Download job details (PDF)](https://developer.yoprint.com/reference/jobs/v1-job-export-job-detail.md)

`POST /v1/api/store/{team_slug}/shipment/export_packing_slip` [Download packing slips (PDF)](https://developer.yoprint.com/reference/shipments/v1-shipment-export-packing-slip.md)

These answer with the file itself, not JSON. Save the response body.

## Import service pricing

The public API imports one kind of CSV file: **service pricing**, a decoration service's price table. An import either replaces the prices of a service you have (`target_mode` `existing_service`), or creates a new service with those prices (`create`).

1. Download an example file to fill in:

   `PUT /v1/api/store/{team_slug}/csv/template/service_pricing` [Download the service pricing CSV template](https://developer.yoprint.com/reference/csv-imports/v1-csv-template-service-pricing.md)

   ```csv
   qty/type,1 Color,2 Colors
   1,12.50,15.00
   24,8.00,10.00
   ```

   - **The file**: a UTF-8 `.csv`, at most 6,240 KB and 600 data rows.
   - **Headers**: exactly `qty/type` first, then one column per option, such as `1 Color`.
   - **Rows**: one price break each, starting with its quantity. The first row's is 1, and no quantity repeats.
   - **Prices**: every cell has one, a decimal number with at most 3 decimal places (`12.50`), or two separated by a bar, `first|additional` (`12.50|9.00`).
   - **Writing numbers**: a digit before the point (`0.50`, not `.50`), and no leading or trailing spaces. See [Money and quantities](https://developer.yoprint.com/concepts/money-and-quantities.md).

2. Upload it as a form (`multipart/form-data`, not JSON):

   `POST /v1/api/store/{team_slug}/imports/csv` [Import a service's price table from CSV](https://developer.yoprint.com/reference/csv-imports/v1-imports-csv-store.md)

   To replace an existing service's prices:

   ```bash
   curl https://secure.yoprint.com/v1/api/store/your-team/imports/csv \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Accept: application/json" \
     -H "X-Yoprint-Decimal-Format: exact-v1" \
     -F entity_type=service_pricing \
     -F target_mode=existing_service \
     -F service_id=V4keXwwxMQyXw7 \
     -F file=@service-pricing.csv
   ```

   To create a new service instead, send `target_mode=create` with its `name`, `allow_cust_loc` (`1` or `0`), `pricing_type` (`single`, `additional` or `single_flat`), and at least one print location in `locations`:

   ```bash
   curl https://secure.yoprint.com/v1/api/store/your-team/imports/csv \
     -H "Authorization: Bearer YOUR_API_KEY" \
     -H "Accept: application/json" \
     -H "X-Yoprint-Decimal-Format: exact-v1" \
     -F entity_type=service_pricing \
     -F target_mode=create \
     -F "name=Screen print, 1 color" \
     -F allow_cust_loc=1 \
     -F pricing_type=single \
     -F "locations[0][id]=BkzhmfgkK2HFTs" \
     -F file=@service-pricing.csv
   ```

   Location ids come from your team's print locations:

   `GET /v1/api/store/{team_slug}/service_location` [List service locations](https://developer.yoprint.com/reference/service-locations/v1-service-location-index.md)

   No public operation lists services, so take an existing service's id from a job's services.

3. Read the answer. The import runs while you wait, and the answer is the finished import. It's all or nothing:

   - **`422`**: the file broke a rule. The answer names the first problem found (with its line, for a row problem), and nothing is saved.
   - **`500`** (`{ "message": "Server Error" }`), also saving nothing: a price or quantity written like `.50` or with a leading space, or a second upload for the same service (or the same new service name) that waited 45 seconds while the first was still running.

   Fix the file, or wait, and upload it again.

   To read an import again later:

   `GET /v1/api/store/{team_slug}/imports/{hash_id}` [Get an import job](https://developer.yoprint.com/reference/csv-imports/v1-imports-show.md)

Replacing an existing service's prices replaces its whole price table: options and price breaks missing from the file are removed. Service pricing imports never create a file of failed rows, so the error-file operation answers `404` for them.

> **Note:** Customers, vendors and products are imported in the web app's import portal, not through the public API. The API offers their example files, so you can prepare data in the right shape:
>
> `PUT /v1/api/store/{team_slug}/csv/template/customer` [Download the customer CSV template](https://developer.yoprint.com/reference/csv-imports/v1-csv-template-customer.md)

## Pitfalls

- **CSV cell errors stop the whole import.** Prices must be decimal numbers with at most 3 decimal places (such as `12.50`). A cell with an invalid price, missing value, duplicate quantity, or more than 3 decimal places answers `422` with the line number and saves nothing. Formatting traps that cause `500` server errors: writing `.50` without a leading `0`, or including spaces around numbers.
- **Upload and request errors.** Missing required parameters (`service_id` for `existing_service`, or `name`, `pricing_type`, `allow_cust_loc` and `locations` for `create`) fails with `422`. Uploading while another import runs on the same service can time out with `500` after 45 seconds. Service pricing imports never create a file of failed rows. Downloading one answers `404`.
- **Downloads aren't JSON, until they fail.** Export downloads, CSVs and PDFs answer with the file, but an error, or a quick export with nothing in range, answers JSON. Check the status first.
- **Wait for `ready`.** A download before then answers `404`. That doesn't mean the export is lost.
- **One of each kind at a time.** Starting a second sales order export while one runs answers `422` with `duplicate_report`.
- **Uploads are forms.** The CSV import takes `multipart/form-data`, not JSON.
- **Dates are UTC.** A range that should cover whole days in the shop's timezone has to be converted.
