Purchase Order Object Reference
Full field list and exact data types for the Purchase Order object returned by
List Purchase Orders (batchwise) and
Get Purchase Orders by ID (data.purchase_orders[]). Use this
page when building a fixed structure on your side (e.g. an SAP staging table) instead of inferring
types from example values.
Types: string, decimal (JSON number, may have a fractional part), boolean, datetime,
object, array. Nullable means the value can be null.
datetime values are RFC 3339 strings in UTC, with or without fractional seconds (up to 6
digits): 2026-10-01T00:00:00Z, 2026-10-01T05:11:52.265968Z. Parse both forms.
Response stability
- Every PO object carries all fields listed below, with
null for missing values. The only
exception is sku_details[].invoiced_quantity, which is omitted when not set. - Fields are never renamed, removed or changed to a different type without advance notice.
- New fields may be added over time. Ignore fields you don't recognise; don't fail on them.
- Key order in the JSON is not significant.
Identifiers — which field is the primary key
| Field | Type | Nullable | Notes |
|---|
po_id | string (UUID) | No | The primary key. System-generated, immutable, globally unique. Key your records on this, and use it in every call that takes a PO reference (getOrders, acknowledge, saveOrder). |
po_number | string | No | The channel's human-readable PO number (e.g. PO-2026-00123). Use it for display and for matching against the PO document. It is not guaranteed unique (two channels can issue the same number) and can be corrected after creation, so don't use it as a key. |
reference_number | string | Yes | Sales Order reference written back by an ERP integration once an SO is created. Separate from both of the above. |
Status — status vs readable_status
| Field | Type | Nullable | Notes |
|---|
status | string | No | Fixed system status. One of NEW, UNCONFIRMED, ASSIGNED, HOLD, FULFILLED, CANCELLED, EXPIRED (lifecycle below). Same set on every account — key business logic on this. |
readable_status | string | Yes | Display label shown in the Openleaf dashboard. Equal to status (e.g. NEW) unless the account has configured custom labels per channel/team. |
The filters.status request field matches readable_status. Use the values returned by
Purchase Order Filters when filtering; on accounts without custom
labels these are the same as the status values.
status | Meaning | Open? |
|---|
NEW | Received, not yet assigned to a dispatching warehouse | Yes |
UNCONFIRMED | Received but awaiting confirmation. Only used on accounts with PO confirmation enabled; most accounts never see it | Yes |
ASSIGNED | Assigned to a dispatching warehouse | Yes |
HOLD | Put on hold. Releasing the hold moves it back to NEW | Yes |
FULFILLED | A shipment has been created against the PO | No |
CANCELLED | Cancelled | No |
EXPIRED | Expired before fulfilment | No |
Change tracking
See Acknowledge Purchase Orders for how these are used.
| Field | Type | Nullable | Notes |
|---|
sync_hash | string | No | Fingerprint of the PO's business fields. Changes whenever status, SKUs/quantities/prices, amounts, addresses, FC code, dates, warehouse assignment, SO or invoice number etc. change. |
acknowledged_at | datetime | Yes | When you last acknowledged this PO. null = never acknowledged. |
is_updated_since_acknowledged | boolean | No | true if the PO changed after you acknowledged it. Always false for never-acknowledged POs. |
Full field reference
Core
| Field | Type | Nullable |
|---|
po_id | string (UUID) | No |
user_id | string (UUID) | No |
po_number | string | No |
reference_number | string | Yes |
status | string | No |
readable_status | string | Yes |
channel | string | Yes |
batch_number | string | Yes |
note | string | Yes |
created_at | datetime | No |
received_date | datetime | Yes |
release_date | datetime | Yes |
expires_at | datetime | Yes |
so_date | datetime | Yes |
transaction_type | string | Yes |
so_date is when the Sales Order in reference_number was recorded. transaction_type is only
set on accounts that classify channels (e.g. Ecom / Modern Trade) and is null otherwise.
Customer (ship-to)
fc_code is the channel's own code for the delivery location (fulfilment centre / dark store), as
printed on the PO.
| Field | Type | Nullable |
|---|
customer_warehouse_id | string (UUID) | Yes |
customer_warehouse_name | string | Yes |
fc_code | string | Yes |
customer_name, customer_address, customer_city, customer_state, customer_pincode, customer_email, customer_phone, customer_gstin, customer_pan | string | Yes |
Billing (bill-to)
| Field | Type | Nullable |
|---|
customer_billing_name, customer_billing_address, customer_billing_city, customer_billing_state, customer_billing_pincode, customer_billing_email, customer_billing_phone, customer_billing_gstin, customer_billing_pan | string | Yes |
Vendor
| Field | Type | Nullable |
|---|
vendor_name, vendor_address, vendor_city, vendor_state, vendor_pincode, vendor_email, vendor_phone, vendor_gstin, vendor_pan | string | Yes |
Vendor fields are free text per PO, not a vendor-master reference. Match on vendor_gstin if you
need a stable vendor key.
Amounts & quantity
| Field | Type | Nullable |
|---|
quantity | decimal | No |
total_amount | decimal | No |
taxable_amount, tax_amount, mrp_amount | decimal | Yes |
applicable_tax | array of string | No (may be empty) |
How to read the amounts:
taxable_amount is the PO total before tax and tax_amount the total tax. Use these two for
the PO value. total_amount follows the channel's PO document and may or may not include
tax depending on the channel, so don't build on it.applicable_tax lists the tax components that apply to the PO (igst, cgst, sgst,
cess). Only use the per-SKU components listed here.
sku_details — array of line items
| Field | Type | Nullable |
|---|
id | string (UUID) | Yes |
code | string | No |
name | string | No |
channel_code | string | No |
hsn | string | Yes |
description | string | Yes |
ean, original_ean | string | Yes |
type, category, subcategory, channel | string | Yes |
is_active | boolean | Yes |
quantity, cost_price, price, mrp | decimal | No (0 when not provided) |
taxable_amount, tax_amount | decimal | No (0 when not provided) |
cgst, cgst_percentage, sgst, sgst_percentage, igst, igst_percentage, cess, cess_percentage | decimal | No (0 when not provided) |
price_difference, mrp_difference, cgst_difference, sgst_difference, igst_difference, cess_difference | decimal | No (0 when not provided) |
original_quantity, delivered_quantity, fill_rate_percentage, gmv_loss, dn_quantity | decimal | Yes |
invoiced_quantity | decimal | Yes (omitted when not set) |
dn_reason | string | Yes |
length, breadth, height, weight | decimal | Yes |
inventory | object | Yes |
remarks | array of object (id, by, timestamp, title, description) | Yes |
On POs received from sales channels (Blinkit, Zepto, Swiggy Instamart, BigBasket, Flipkart
etc.), per-line amounts are per unit, not line totals: cost_price is the unit price before
tax (same as the line's taxable_amount), and tax_amount, cgst, sgst, igst, cess are
the tax per unit. Multiply by quantity for the line total. price depends on the channel (on
some it is the tax-inclusive unit price), so prefer cost_price + tax_amount.
Assignment & fulfilment
| Field | Type | Nullable |
|---|
assigned_warehouse_id | string (UUID) | Yes |
sap_whs_code | string | Yes (only set for SAP Business One–synced POs) |
is_assigned, is_fulfilled, has_draft_order | boolean | No |
is_processed_by_warehouse, is_grn_received, is_appointment_confirmed | boolean | Yes |
po_processed_by_warehouse_at, appointment_scheduled_at | datetime | Yes |
Assigned warehouse
| Field | Type | Nullable |
|---|
warehouse_name, warehouse_manager_name, warehouse_email, warehouse_phone, warehouse_address_line_1, warehouse_address_line_2, warehouse_city, warehouse_state, warehouse_pincode, warehouse_country | string | Yes |
warehouse_is_enabled | boolean | Yes |
Invoice, shipment & documents
| Field | Type | Nullable |
|---|
invoice_number, invoice_id, ewaybill_number, master_waybill, asn_number, shipping_label_url | string | Yes |
invoice_date | datetime | Yes |
invoice_data, uploaded_documents | JSON (array or object) | Yes |
child_waybills | array of string | Yes |
Other
| Field | Type | Nullable |
|---|
custom_data | object (free-form, account-specific keys) | Yes |
remarks | array of object (id, by, timestamp, title, description) | No (may be empty) |
swiggy_idempotency_id | string | Yes |
custom_data.openleaf_sync is reserved for acknowledgement bookkeeping — read the top-level
acknowledged_at / is_updated_since_acknowledged fields instead of parsing it.