---
title: "Bulk create or update purchase orders"
method: POST
path: "/purchase-orders/bulk"
tags: ["purchaseOrders"]
---

# Bulk create or update purchase orders

`POST /purchase-orders/bulk`

Creates or updates multiple purchase orders in a single request. Maximum 100 purchase orders and 1000 total line items per request. Processing is always asynchronous via Celery and returns a batch tracking ID for polling progress.

## Headers

- `Authorization` string, required
- `X-Api-Key` string, required
- `X-API-Version` string
- `Idempotency-Key` string

## Request body

- PurchaseOrderBulkUpsertRequest
  - `ottimate_company_id` integer, required — Company ID for all purchase orders in this batch
  - `ottimate_location_id` integer, required — Default location ID for all purchase orders in this batch
  - `purchase_orders` PurchaseOrderBulkUpsertRequestPurchaseOrdersItems[], required — Max 100 POs with max 1000 total items
    - `external_id` string
    - `po_number` string, required
    - `erp_vendor_id` string, required — External vendor identifier from ERP system
    - `erp_vendor_name` string — Vendor name from ERP system
    - `date` string, date, required
    - `total_amount` number, double, required
    - `tax` number, double
    - `freight` number, double
    - `miscellaneous_charges` number, double
    - `invoice_number_ref` string — Invoice reference number
    - `is_2_way` boolean — Whether this is a 2-way match PO
    - `items` PurchaseOrderBulkUpsertRequestPurchaseOrdersItemsItemsItems[], required
      - `external_id` string
      - `name` string, required
      - `sku` string
      - `quantity` number, double, required
      - `price` number, double, required
      - `uom` string
      - `dimensions` PurchaseOrderBulkUpsertRequestPurchaseOrdersItemsItemsItemsDimensions — Dimension values keyed by type (e.g. PROJECT, CLASS). Each value is the ERP external_id of the dimension. When provided, REPLACES all existing dimensions on the item — include every dimension you want to keep. Omit the field or pass null to leave dimensions unchanged. Passing {} returns 400.
    - `custom_fields` PurchaseOrderBulkUpsertRequestPurchaseOrdersItemsCustomFields — Optional custom fields configured for this company's purchase orders. Provide field values, pass null, or omit the field entirely — passing {} (empty object) returns 400.

## Response `202`

Asynchronous processing started. Poll batch_url for progress.

- BulkUpsertAsyncResponse
  - `version` string
  - `status` 'processing', required
  - `batch_id` string, required
  - `batch_url` string
  - `results_url` string
  - `summary` BulkUpsertAsyncResponseSummary
    - `count` integer
    - `total_items` integer

## Other responses

- `400` — Bad request - Invalid parameters or request format
- `403` — Forbidden - Access denied or insufficient permissions

---

[API](https://skmtc.dev/ottimate/apis/api-reference.md) · [All operations](https://skmtc.dev/ottimate/apis/api-reference/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/ottimate/api-reference/revisions/ba91ff4c6969/schema)
