# Export invoice line items in a date range to CSV

```http
POST https://secure.yoprint.com/v1/api/store/{team_slug}/data_export/export_invoice_item
```

- API version: v1
- Operation ID: `v1_public_data_export_export_invoice_item`
- Section: Imports and exports › [Data exports](https://developer.yoprint.com/reference/data-exports.md)
- Web page: https://developer.yoprint.com/reference/data-exports/v1-data-export-export-invoice-item/
- Authentication: `Authorization: Bearer YOUR_API_KEY`

Starts a CSV export with one row per invoice line item: the order, invoice, job, product, services, quantity and totals. It runs in the background: the response is the new export with `status` `pending`.

As with the invoice export, the range applies to the **order's issue date**, and deleted invoices and draft or deleted orders are left out. `product_cost`, `product_total`, and `service_total` have 3 decimal places. `subtotal` and `total` have 2 decimal places, and `total` always matches `subtotal` because line items do not include tax or discounts. `quantity` is a plain number such as `24`. `status` repeats `order_status`.

## Permission

Any member of the team. Exports do not check the role: every member can export every type, including sales orders, invoices, payments and tax, even a member whose role can't see them in the web app.

## Business rules

- `start_date` and `end_date` are required, and `end_date` can't be before `start_date`. There is no maximum range. There is no `date_field`: the range is always the order's issue date.
- One export of each type at a time for the whole team: while one is `pending` or `generating`, creating another of the same type is a 422 (`duplicate_report`).
- At most 3 exports pending or generating per team, of any type. A fourth is a 429 (`export_capacity`).
- `columns`, when sent, must be a non-empty list of column ids for this type. `GET /v1/api/store/{team_slug}/data_export/definitions` lists them with their labels.
- `idempotency_key`, when sent, must be a string of at most 100 characters. Reusing a key with a different type, different settings or another person is a 422.

## Side effects

- Queues the export and returns it at once with `status` `pending`. The file is built in the background.
- People with the Download Center open see it appear and update live. No email is sent.
- The file is kept for 3 days after it is ready, then deleted. The export stays in the list as history.

## Path parameters

- `team_slug` (string, required): Your team slug, shown as Team ID next to your API key in Profile. Example `your-team`.

## Request body

Content type `application/json`, required.

Fields:

- `end_date` (string (date-time), required): End of the range, inclusive, on or after `start_date`. A date alone means midnight UTC at its start, which leaves that day out, so send the last moment of the day: `2026-10-31T23:59:59.999Z`.
- `start_date` (string (date-time), required): Start of the range, inclusive: a date and time in UTC, such as `2026-10-01T00:00:00Z`. An offset (`2026-10-01T00:00:00-07:00`) is accepted and converted to UTC; a value with neither is read as UTC.
- `columns` (array of strings, optional): The columns to include, in the order you want them in the file. Omit it for every column in the default order. Repeats are dropped. One of: `"order_id"`, `"order_name"`, `"order_status"`, `"order_date"`, `"order_creator"`, `"order_warehouse"`, `"invoice_id"`, `"status"`, `"job_name"`, `"services"`, `"product_type"`, `"product_name"`, `"product_cost"`, `"product_total"`, `"service_total"`, `"quantity"`, `"subtotal"`, `"total"`. At least 1 item. Items must be unique.
- `idempotency_key` (string, optional): Optional, a string of up to 100 characters. Send the same key to retry the same request safely: you get the export it created, and nothing new starts while that export is `pending`, `generating`, `ready`, or failed with `export_generation_failed`. If it failed with `export_queue_failed` or `export_attempt_expired`, the same key queues it again. Keys are shared by the whole team. At most 100 characters.

### Example request

```bash
curl -X POST 'https://secure.yoprint.com/v1/api/store/your-team/data_export/export_invoice_item' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "end_date": "2026-10-31T23:59:59.999Z",
    "idempotency_key": "export-invoice-item-list-2026-10",
    "start_date": "2026-10-01T00:00:00Z"
  }'
```

## Response

`200` Successful response.

Fields:

- `data` (object, required)
  - `config` (object | null, required): What you asked for: the dates (and `date_field`) as you sent them, and the `columns` you chose, without repeats, with `schema_version`. Null for customer and product exports sent without `columns`.
    - `columns` (array of strings, optional): Only when you chose columns.
    - `date_field` (string, optional): Sales order exports only.
    - `end_date` (string, optional)
    - `schema_version` (integer, optional): Only when you chose columns.
    - `start_date` (string, optional)
  - `created_at` (string (date-time), required)
  - `created_by` (integer | null, required): A plain number for the person who created it. It is not a hash id and no operation takes it; show `creator.name` instead.
  - `creator` (object | null, required): The person who created it; null if their account was deleted. More of their account fields may appear; rely only on these.
    - `email` (string, optional)
    - `id` (integer, optional): The same plain number as `created_by`, not a hash id.
    - `name` (string, optional)
  - `error_code` (string | null, required): Why it failed. `export_generation_failed`: the file could not be built; create a new export. `export_attempt_expired`: it did not finish in time. `export_queue_failed`: it could not be queued. Null unless `status` is `failed`. One of: `"export_generation_failed"`, `"export_attempt_expired"`, `"export_queue_failed"`.
  - `expires_at` (string (date-time) | null, required): When the download stops working: 3 days after the export became ready. The file is then deleted; the export stays in the list but can't be downloaded.
  - `file_size` (integer | null, required): File size in bytes, once ready.
  - `id` (string, required): The export id. Use it to poll and to download.
  - `progress` (object, required)
    - `completed` (integer, required): Rows written so far, not counting the header row. Updated every 500 rows; product exports update only at the end.
    - `total` (integer | null, required): Rows in the file, not counting the header row. Null until the export is ready: there is no total to show a percentage against while it runs.
  - `status` (string, required): `pending`: queued. `generating`: running. `ready`: the file can be downloaded. `failed`: see `error_code`. One of: `"pending"`, `"generating"`, `"ready"`, `"failed"`.
  - `type` (string, required): One of: `"customer_list"`, `"sales_order_list"`, `"invoice_list"`, `"invoice_item_list"`, `"payment_list"`, `"purchase_order_list"`, `"tax_list"`, `"simple_product_list"`, `"variant_product_list"`.
  - `updated_at` (string (date-time), required)
- `status` (integer, required)
- `success` (boolean, required)

Example response:

```json
{
  "data": {
    "config": {
      "end_date": "2026-10-31T23:59:59.999Z",
      "start_date": "2026-10-01T00:00:00Z"
    },
    "created_at": "2026-11-03T09:30:00+00:00",
    "created_by": 1001,
    "creator": {
      "email": "alex@example.com",
      "id": 1001,
      "name": "Alex Rivera"
    },
    "error_code": null,
    "expires_at": null,
    "file_size": null,
    "id": "G59hl28azOL22k",
    "progress": {
      "completed": 0,
      "total": null
    },
    "status": "pending",
    "type": "invoice_item_list",
    "updated_at": "2026-11-03T09:30:00+00:00"
  },
  "status": 200,
  "success": true
}
```

## Errors

- `401` The API key is missing or wrong (`unauthenticated`).
- `403` You are not allowed to do this. `api_access_not_in_plan`: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only `{"message": "Insufficient Permission"}`. Other refusals use the error envelope, with `unauthorized` or `user_is_not_authorized`.
- `404` Not found. `page_not_found`: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. `code: null`, with a message that starts "No query results for": there is no record with that id.
- `422` The request was refused and changed nothing. `validation_failed` lists what to fix in `error.fields`; other codes name a business rule, explained in `error.message`. A few checks answer with only `{"message": "..."}`. An operation that calls another service (a payment provider, a carrier, an accounting app) can answer `connect_timeout` ("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.
  - When: Dates are missing, not dates, or out of order, or, for sales orders, `date_field` is missing or not one of the six (`invalid_export_request`, "Choose valid, ordered export dates and a supported date field."). Fix: Send both dates, with `end_date` on or after `start_date`, and for sales orders a `date_field` from the list.
  - When: A column id is unknown, `columns` is not a list, or it is empty (`invalid_export_request`, "The selected export columns are invalid." or "Select at least one export column."). Fix: Use ids from the definitions operation, or omit `columns`.
  - When: `idempotency_key` is not a string or is longer than 100 characters (`invalid_export_request`, "The export request key is invalid."). Fix: Send a string of up to 100 characters, or leave it out.
  - When: An export of this type is already pending or generating for your team (`duplicate_report`, "There is already a report being generated. Please wait until the report is fully generated"). Fix: Poll the running export and download it instead, or wait until it finishes.
  - When: The `idempotency_key` was used for a different export, or by another member (`invalid_export_request`, "This request key was already used for a different export."). Fix: Use a new key for a new export.
- `429` Your team already has 3 exports pending or generating.
  - When: Three exports are already pending or generating (`export_capacity`). Fix: Wait for one to finish, then try again.
- `503` Exports are paused for a few minutes during a YoPrint update, or the export could not be queued (`export_queue_failed`, "The export could not be queued. Try again.").
  - When: Exports are paused during an update (`export_creation_paused`, with `Retry-After: 120`), even for a retry with the same key; or the export could not be queued (`export_queue_failed`, no `Retry-After`). Fix: When paused, wait the `Retry-After` seconds and send the request again. After a queue failure, send it again with the same `idempotency_key` to queue the same export.

## Notes

- The range is inclusive at both ends and is compared in UTC, with times. To cover whole days in your team's time zone, convert its midnights to UTC, or send the offset: `2026-10-01T00:00:00-07:00` to `2026-10-31T23:59:59.999-07:00`. There is no maximum range.
- Amounts in the file are decimal strings with no currency symbol: `product_cost`, `product_total`, and `service_total` have 3 decimal places (such as `5.125`); `subtotal` and `total` have 2 decimal places (such as `125.00`).
- Dates in the file are `YYYY-MM-DD`, in your team's time zone.
- The full flow: create the export, poll `GET /v1/api/store/{team_slug}/data_export/{id}` every few seconds until `status` is `ready` (or `failed`), then download the file with `GET /v1/api/store/{team_slug}/data_export/{id}/download` within 3 days.
- Retrying with the same `idempotency_key` returns the same export and starts nothing new while it is `pending`, `generating`, `ready`, or failed with `export_generation_failed` (send a new key to try that one again). After `export_queue_failed` or `export_attempt_expired`, the same key queues the same export again, and the one-per-type (422 `duplicate_report`) and three-per-team (429 `export_capacity`) limits apply.
- The file is CSV, UTF-8 with a byte order mark. Its first row holds the column ids, such as `order_id` (not the labels), in the order of `columns` or, without it, the default order. An export with no matching records has only that row.

## Related

- [Get a data export](https://developer.yoprint.com/reference/data-exports/v1-data-export-show.md): `GET /v1/api/store/{team_slug}/data_export/{id}`
- [Download a data export's file](https://developer.yoprint.com/reference/data-exports/v1-data-export-download.md): `GET /v1/api/store/{team_slug}/data_export/{id}/download`
- [List export types and their columns](https://developer.yoprint.com/reference/data-exports/v1-data-export-definitions.md): `GET /v1/api/store/{team_slug}/data_export/definitions`
- [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_invoice`
