APIOrder

Upsert an order

POST
/public/v1/orders

Create or update a single sales order. Omit id to create a new order (Distru assigns the order number and id); pass an existing order's id to update it. An id that doesn't exist, or belongs to another company, returns a not-found error.

Updates are sparse at the top level: send only the fields you want to change — every field you omit (including status) keeps its current value. The items and charges collections work differently. Omit the whole items (or charges) field and its existing rows are left untouched. Send the field and it fully replaces that set: an existing row whose id you omit is deleted, an entry whose id matches an existing row updates it (omitted fields on that entry are kept from the existing row, so you can patch a single field by sending just its id and the change), and an entry whose id is new (or omitted — Distru then assigns one) is added. Sending items as an empty array removes every line, which an order cannot be left in, so it is rejected. Charges are full-replace on update — see the charges field for how existing charges, presets, and deletions are handled; on create, charge presets whose auto-apply tags match the order's products are added automatically. The whole upsert is atomic: if any part is rejected (a validation error, a disallowed status transition, an unfulfillable line) nothing is changed and the response is a 400 whose errors point at the offending field.

Setting status moves the order through its lifecycle and drives inventory. A PENDING or CANCELED order reserves nothing and touches no inventory; moving to PROCESSING commits sellable inventory — assigning a package or batch to a line item moves that quantity into a committed selling state and any unfulfilled line adds to the product's reserved quantity; READY_TO_SHIP, DELIVERING, DELIVERED, and COMPLETED additionally require every line item to be fulfilled. See the status field on the order for the full per-status behavior and transition requirements.

Compliance: associating the order with a state transfer is one-system-only — send metrc_transfer_id OR biotrack_id, never both. Doing so builds the order from that outgoing Metrc/BioTrack transfer. Any order carrying package-tracked items must be associated with a compliance transfer before it can reach DELIVERING, DELIVERED, or COMPLETED.

Invoicing: set upsert_invoice to create or refresh this order's invoice, and email_invoice to email it. The response returns the saved order with its recomputed total, line items (with cost fields), charges, invoices, and returns.

Required permission: orders_permissions_create to create, orders_permissions_edit (plus access to the order under team restrictions) to update.

Authorization

Bearer
headerAuthorizationBearer <token>

API token generated in Distru (Settings → Distru API). Send it on every request as Authorization: Bearer YOUR_API_TOKEN.

Request Body

application/json
  1. body
biotrack_id?string

The BioTrack manifest to associate with this order, building the order from that outgoing BioTrack transfer. Mutually exclusive with metrc_transfer_id — send at most one; an order can be linked to only one compliance transfer.

blaze_payment_type?string

The payment type for an order shipping to a Blaze-associated company. Required (and only meaningful) when the order's buyer company is mapped to a Blaze retailer through the Distru integration; leave it off otherwise.

Value in"CASH""CREDIT""DEBIT""COD""ACH_TRANSFER""CHEQUE""OTHER"
company_id?string

The buyer of this order, as a company relationship ID (the same id in each order's company.id and GET /public/v1/companies). Determines the customer, and drives pricing, default payment term (used to derive due_datetime), and blaze_payment_type requirements. Optional while the order stays PENDING, PROCESSING, or CANCELED, but required to move it to READY_TO_SHIP, DELIVERING, DELIVERED, or COMPLETED. Once a customer is set, you cannot clear it back to null on a later update.

delivery_datetime?string

ISO8601 datetime the order was / will be delivered. Optional; null when the order has no delivery datetime set.

due_datetime?string

The datetime by which the customer is expected to pay for this order. Optional: when omitted, it is derived from the customer's default payment term, then the company default order payment term, then falls back to the order date (COD).

external_notes?string

This is a message that will be shown to the customer on order slips. This is the "Message to Customer" field in the Distru order form.

upsert_invoice?boolean

When true, create an invoice for this order if it doesn't have one yet, or update the existing invoice with the order's latest changes.

email_invoice?boolean

When true, email the order's invoice. No email is sent unless the order has an invoice (see upsert_invoice) and a recipient can be resolved from email_invoice_addresses or the buyer company relationship's invoice email.

email_invoice_addresses?string

Comma-separated list of email addresses to send the invoice to when email_invoice is true. Takes precedence over the company relationship's invoice email. Invalid addresses are rejected.

id?string

ID for this order. Omit it to create a new order — Distru assigns the ID. Provide an existing order's ID to update that order; an ID that doesn't exist returns a not-found error.

order_datetime?string

ISO8601 datetime the order was placed. Required on create — omitting it there returns a validation error; on update, omit to leave the existing value unchanged. This is the field the list endpoint sorts (newest first) and filters on.

charges?array<>

The order's complete set of charges and discounts — the extra lines (fees, discounts, taxes) added on top of the items. Each entry follows the OrderChargeRequest shape.

Replacement semantics: on update the charges you send become the order's entire set — any existing charge you don't include is deleted. Omit the charges field entirely to leave the current charges unchanged; send [] to remove them all. Each charge is matched to an existing one by id — a charge with a new or absent id is added, a charge with a known id is updated, and on a matched charge any field you omit keeps its current value — so sending just {"id": "..."} keeps that charge exactly as it is. You never need to resend a charge's full payload to preserve it; include its id and only the fields you want to change.

Manual charges: provide name, type, unit_type, and the amount. For a PERCENT charge the amount is calculated from the order subtotal and any price you send is ignored; send percent instead.

Preset charges: set charge_preset_id and leave name and type out — Distru fills them from the charge preset. If the preset allows inline edits you may also set unit_type and the amount (price or percent); otherwise send only charge_preset_id. Sending a field the preset controls returns a 400.

charge_preset_id is fixed once a charge exists. Omitting charge_preset_id on an update keeps the charge's current preset, like any other omitted field. But you cannot change it: sending a different charge_preset_id, sending null to detach a preset, or adding a charge_preset_id to a charge that was manual all return a 400. To switch a charge to a different preset or to a manual charge, drop it (omit its id, which deletes it) and add a new charge.

Auto-apply: on order create, presets whose auto-apply tags match the order's products are added automatically (see the preset's auto_apply_tags_on_sales_orders flag); a preset you also send explicitly is applied once. Updates never auto-apply: adding items later does not add new preset charges (existing PERCENT charges still recompute from the new subtotal; fixed-price ones are unchanged). The reverse also holds: an auto-applied charge is never auto-removed — deleting the line items whose product tags added it leaves the charge in place until you drop it yourself (omit its id from a charges update).

items?array<>

The products being sold on this order, one entry per line. Required on create. On update it is optional: omit it to leave the order's existing lines untouched, or send it to fully replace the line set — an existing line whose id you omit is deleted, an entry whose id matches an existing line updates it (fields you omit on the entry keep their current value, so an id-only entry is a no-op), and an entry with a new or omitted id is added. An empty array is rejected because every order must keep at least one item. If the order is matched with a compliance transfer, its package-tracked lines each map one-to-one to a transferred package and cannot be deleted — omitting one is rejected. Each entry follows the OrderItemRequest shape.

internal_notes?string

Free-form notes visible only inside Distru; never shown to the customer. Use external_notes for a customer-facing message.

metrc_transfer_id?integer

The Metrc transfer to associate with this order, building the order from that outgoing Metrc transfer. This is Metrc's own integer transfer id, not a Distru ID. Mutually exclusive with biotrack_id — send at most one; an order can be linked to only one compliance transfer.

location_id?string

The Distru location ID of the order's top-level location — used to filter orders and to associate the order with a compliance (Metrc) license, identifying which Metrc license the order takes place under. It is not the location sale quantities are drawn from; each order item sets that via its own location_id. Optional in general, but required when the order has any package-tracked items, where it must reference a location that has a compliance license and that license must match the license of every package-tracked item's location.

billing_location_id?string

The billing location's ID (a Distru location ID). Optional.

shipping_location_id?string

The shipping location's ID (a Distru location ID). Optional.

owner_id?string

The Distru user that owns this order, as a user ID (the same id in the response's owner.id). Optional.

status?string

The status to set for this order, controlling where it sits in its lifecycle and how it affects inventory and compliance. Required on create; on update, omit to leave the current status unchanged. See the status field on the order response for what each value means. Note that some transitions have requirements: moving to READY_TO_SHIP, DELIVERING, DELIVERED, or COMPLETED requires every line item to be fulfilled and a customer (company_id) to be set, and DELIVERING/DELIVERED/COMPLETED additionally require a compliance transfer when the order carries any package-tracked items.

Value in"PENDING""PROCESSING""READY_TO_SHIP""DELIVERING""DELIVERED""COMPLETED""CANCELED"
custom_data?

A map of custom field IDs to their values. Use GET /public/v1/custom-fields?parent_object=order to retrieve available custom fields, their IDs, and their types. The value format depends on the field's type: a text field takes a string, a date field takes a full ISO8601 datetime, and a checkbox field takes an array of its selected options.

Response Body

A single order

*/*
  1. response

A single order wrapped in a data envelope

data?

A sale of products to a customer. Holds the line items sold, their quantities and prices, any extra charges/discounts/taxes, delivery and fulfillment details, and links to the resulting invoices and returns.

Example Request
curl -X POST 'https://app.distru.com/public/v1/orders' \  -H 'Content-Type: application/json' \  -d '{  "billing_location_id": "00000000-0000-0000-0000-0000000004e9",  "charges": [    {      "name": "C1",      "percent": "10.0000",      "type": "CHARGE",      "unit_type": "PERCENT"    },    {      "name": "C2",      "price": "-5.0000",      "type": "DISCOUNT",      "unit_type": "PRICE"    }  ],  "company_id": "00000000-0000-0000-0000-000000000921",  "delivery_datetime": "2020-01-01T00:00:00.000000Z",  "due_datetime": "2020-01-01T00:00:01.000000Z",  "external_notes": "Thank you for ordering!",  "internal_notes": "Internal notes for this order",  "items": [    {      "location_id": "00000000-0000-0000-0000-0000000004e8",      "price_base": "10.000000000",      "product_id": "29aa99b0-36f0-46a1-ab81-3e03a2f54abf",      "quantity": "1.000000000"    }  ],  "order_datetime": "2020-01-01T00:00:02.000000Z",  "owner_id": "00000000-0000-0000-0000-000000001403",  "shipping_location_id": "00000000-0000-0000-0000-0000000004e9",  "status": "PROCESSING"}'
Example Response
{  "data": {    "billing_location": {      "address": "123 Fake Street, Beverly Hills, CA 90210, US",      "company": {        "id": "00000000-0000-0000-0000-00000001b73d",        "name": "Company 5147",        "updated_datetime": "2026-11-05T11:00:20.210137Z"      },      "company_id": "00000000-0000-0000-0000-00000002d440",      "id": "00000000-0000-0000-0000-0000000124e0",      "license_id": null,      "license_number": null,      "name": "Place 1722"    },    "biotrack_id": null,    "blaze_payment_type": null,    "buyer_company": null,    "buyer_note": null,    "charges": [      {        "charge_preset": null,        "id": "b0f31b30-1c44-482e-998d-03cbc3faa408",        "inserted_datetime": "2026-11-05T11:00:20.280536Z",        "name": "C1",        "percent": "10.0000",        "price": "1.00",        "type": "CHARGE",        "unit_type": "PERCENT"      },      {        "charge_preset": null,        "id": "ef6f8f12-8403-4497-b047-67e2f26dec42",        "inserted_datetime": "2026-11-05T11:00:20.283215Z",        "name": "C2",        "percent": null,        "price": "-5.00",        "type": "DISCOUNT",        "unit_type": "PRICE"      }    ],    "combined_order": null,    "company": {      "id": "00000000-0000-0000-0000-00000001b73d",      "name": "Company 5147",      "updated_datetime": "2026-11-05T11:00:20.210137Z"    },    "creator": {      "banned": false,      "deleted_at": null,      "email": "[email protected]",      "full_name": "John Foo",      "id": "00000000-0000-0000-0000-00000003eb9a",      "inserted_datetime": "2026-11-05T11:00:20.230818Z",      "role": {        "id": "00000000-0000-0000-0000-0000000409cc",        "name": "Admin 7775"      },      "updated_datetime": "2026-11-05T11:00:20.230818Z"    },    "custom_data": [      {        "id": 2422,        "name": "Custom Field 105",        "value": null      }    ],    "delivered_datetime": null,    "delivery_datetime": "2020-01-01T00:00:00.000000Z",    "due_datetime": "2020-01-01T00:00:01.000000Z",    "external_notes": "Thank you for ordering!",    "id": "042ed508-1c94-4933-be2a-52126f4681ba",    "inserted_datetime": "2026-11-05T11:00:20.279658Z",    "internal_notes": "Internal notes for this order",    "inventory_source": null,    "invoices": [],    "items": [      {        "batch": null,        "compliance_quantity": null,        "cost_per_unit": null,        "cost_per_unit_default": null,        "id": "27ffe0be-f229-4f97-ab8c-c36772442d14",        "inserted_datetime": "2026-11-05T11:00:20.284020Z",        "is_sample": false,        "leaflink_id": null,        "location": {          "address": "123 Fake Street, Beverly Hills, CA 90210, US",          "company_id": "00000000-0000-0000-0000-00000002d43f",          "id": "00000000-0000-0000-0000-0000000124df",          "license_id": "00000000-0000-0000-0000-000000006ea6",          "name": "Place 1721"        },        "note": null,        "package": null,        "price": "10.000000000",        "price_base": "10.000000000",        "price_tier_mode": "AUTO",        "price_tier_version": null,        "product": {          "id": "6113c490-3cff-4ac1-a70b-84b13c1db32d",          "name": "P1",          "sku": "SKU1",          "updated_datetime": "2026-11-05T11:00:20.241537Z"        },        "quantity": "1.000000000",        "returned_quantity": "0",        "thc_percentage_total": null,        "total_cost_actual": null,        "total_cost_default": null      }    ],    "leaflink_id": null,    "leaflink_order_number": null,    "location": null,    "menu": null,    "metrc_transfer_id": null,    "metrc_transfer_template_error": null,    "metrc_transfer_template_id": null,    "metrc_transfer_template_status": null,    "order_datetime": "2020-01-01T00:00:02.000000Z",    "order_number": "SO-0000001",    "owner": {      "banned": false,      "deleted_at": null,      "email": "[email protected]",      "full_name": "John Foo",      "id": "00000000-0000-0000-0000-00000003eb9a",      "inserted_datetime": "2026-11-05T11:00:20.230818Z",      "role": {        "id": "00000000-0000-0000-0000-0000000409cc",        "name": "Admin 7775"      },      "updated_datetime": "2026-11-05T11:00:20.230818Z"    },    "payment_term_name": null,    "returns": [],    "shipping_location": {      "address": "123 Fake Street, Beverly Hills, CA 90210, US",      "company": {        "id": "00000000-0000-0000-0000-00000001b73d",        "name": "Company 5147",        "updated_datetime": "2026-11-05T11:00:20.210137Z"      },      "company_id": "00000000-0000-0000-0000-00000002d440",      "id": "00000000-0000-0000-0000-0000000124e0",      "license_id": null,      "license_number": null,      "name": "Place 1722"    },    "status": "PROCESSING",    "tasks": [],    "total": "6.00",    "updated_datetime": "2026-11-05T11:00:20.314645Z"  }}