Create and change quotes and sales orders
AI agents: read /llms.txt; add .md to any page URL. This page as Markdown: /guides/orders.md. Index of every page and the rules for every call: /llms.txt. Guides and concepts in one file: /llms-full.txt. OpenAPI: /openapi.json.
Read first: Quotes and sales orders, for the draft and published model, statuses, dates, approvals and what else an order carries.
This guide creates and changes the order record itself: its customer, dates, status and the few operations that act on the whole order. Lines belong to the order too, but have their own guide: Add line items. Invoicing, payments and shipping have their own guides too: Invoice an order, Take payments and refunds and Ship an order.
- 1. Create a draftQuote or sales order, with defaults from the team and customer
- 2. Read and find ordersBy id, a filtered list, or a quick search
- 3. Change itHeader fields with update. Everything else with a named action
- 4. PublishCheck
data.valid: a failed publish can still answer200 - 5. Ask the customer to approveOnly once published
- 6. Convert or cloneConvert keeps the id. Clone makes a new draft
- 7. Delete and restoreDeleting also undoes money and stock
- 8. Read the historyWho changed what, and when
1. Create a draft
Section titled “1. Create a draft”/v2/api/store/{team_slug}/v2/sales_order/new
curl https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/new \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json" \ -H "X-Yoprint-Decimal-Format: exact-v1" \ -H "Content-Type: application/json" \ -d '{ "client_note": "Prices valid for 30 days.", "customer_id": "63KcENAAsKuipY", "object_type": "quote" }'object_typeisquoteorsales_order. Sendquote(or the olderquote: true) for a quote. Leave both out for a sales order.customer_idis optional, but an order needs one before it can be published. An id that isn’t one of your customers is ignored, not refused: the draft is created without a customer.- The draft already has its order number (
scoped_id, such asSO1001): your own, if you sendscoped_id, or the team’s next one. - Its other defaults come from the team and the customer: today’s issue date, the customer’s (or team’s) pricing group, payment term and warehouse, the customer’s primary brand profile, the team’s starting status, and due and production dates from the team’s settings.
Add jobs and line items next, then publish.
2. Read and find orders
Section titled “2. Read and find orders”Open one order or quote, with its line items, customer and message participants:
GET Get a sales order or quote/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}
{order_unique_key} takes the order’s id or its unique_key (ord_…). The order number people see, such as SO1001, is not accepted. Deleted orders come back too, with trashed: true.
List sales orders or quotes a page at a time, with filters and sorting. Each lists one kind only:
POST List sales orders/v2/api/store/{team_slug}/v2/sales_order/filter
POST List quotes /v2/api/store/{team_slug}/v2/quote/filter
{ "filters": [{ "key": "status_type", "operator": "in", "value": ["start", "in_progress"] }], "sort": [{ "key": "due_date", "direction": "asc" }]}- Rows come back without line items, each with its
customerrecord, so you don’t need a lookup per row. - See Filtering and sorting for the filter keys, operators and the date-offset rules.
To search by a word or number, for a search box or a picker:
GET Search sales orders and quotes/v2/api/store/{team_slug}/search/orders
curl "https://secure.yoprint.com/v2/api/store/your-team/search/orders?q=northwind"e=0" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json" \ -H "X-Yoprint-Decimal-Format: exact-v1"qmatches anywhere in the order number, order name, customer PO number, customer name, or any part of the addresses. Several words can each match a different field.- Returns at most 30 matches, highest order number first, with no paging.
quote=1searches quotes only,quote=0sales orders only. For aPOSTbody, send"quote": trueorfalseinstead. - For more than 30 results, or for filters, use the list operations above instead.
3. Change an order
Section titled “3. Change an order”Update the header fields directly, sending only what changes:
PUT Update a sales order or quote/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}
{ "customer_po_number": "PO-1190", "is_rush": true, "name": "Spring team shirts"}- This covers
name,scoped_id,customer_po_number,client_note,prod_note,is_rush, the billing and shipping address ids,customer_profile_idandcustomer_contact_id,created_by, andstatus_id. - Its
object_typeandquotefields take only the order’s current value on a draft. They don’t convert it. Use theconvert_to_quoteorconvert_to_sales_orderaction for that. status_idhere changes the status without sending the status webhooks (the change is still recorded in the history). Prefer thechange_statusaction below, which sends them.
For everything else, run a named action, with the action’s name in the path and its fields in the body:
PUT Run an action on a sales order or quote/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/action/{action_name}
curl -X PUT https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/action/change_status \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json" \ -H "X-Yoprint-Decimal-Format: exact-v1" \ -H "Content-Type: application/json" \ -d '{ "value": "52CuV8sWcDZLPm" }'| Purpose | action_name |
Body | Draft | Published |
|---|---|---|---|---|
| Customer and terms | change_customer |
value: customer id |
yes | no |
change_payment_term |
payment_term_id, deposit_due, full_amount_due, optional deposit_total |
yes | yes | |
change_pricing_group |
pricing_group_id |
yes | yes | |
change_pricing_strategy |
pricing_strategy |
yes | yes | |
change_warehouse |
warehouse_id |
yes | yes | |
| Dates | change_issue_date |
value: date. Moves the order’s other dates, jobs and invoices with it |
yes | yes |
change_customer_due_date, change_ship_due_date |
value: date or null |
yes | yes | |
change_deposit_due, change_full_amount_due |
value: date or null |
yes | yes | |
change_start_end_date_date |
start_date, due_date: both or neither |
yes | yes | |
| Status and rush | change_status |
value: a sales order or quote status id, from the status list below |
no | yes |
mark_order_as_rush, mark_order_as_not_rush |
{} |
yes | yes | |
mark_all_job_as_rush, mark_all_job_as_not_rush |
{} |
yes | yes | |
| Convert and clone | convert_to_quote, convert_to_sales_order |
optional status_id, pricing_group_id |
yes | yes |
clone_order |
is_time_shift, cloned_anchor_date, item_choices, optional pricing_group_id |
yes | yes | |
| Sending | send_order_detail |
{} |
no | yes |
send_payment_reminder |
{} |
no | yes | |
send_approval_reminder |
{} |
no | yes |
-
change_customerresets the order’s brand profile, addresses, contact, payment term, warehouse and pricing group from the new customer. It’s refused if the draft already has a payment, a payment link, a sent message or linked stock. -
The
send_*actions email the customer and count toward the team’s daily email limit. Ask before calling one on someone’s behalf. -
Status ids come from the team’s status lists, one for sales orders and one for quotes. Statuses such as “In production” exist in the job list too: use the sales order one for an order.
GET List sales order statuses/v1/ api/ store/ {team_slug}/ setting/ status/ sales_order -
On an order synced from Shopify, only
change_status,change_customer_due_date,change_ship_due_date,change_start_end_date_dateandsend_order_detailwork. Every other action fails with422. -
An action that isn’t allowed for the order’s state (draft or published) fails with
object_action_error“Action not found”.
Two more operations set just a note, and can clear it:
PUT Set the Client Note of a sales order or quote/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/customer_note
PUT Set the production note of a sales order or quote /v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/production_note
{ "customer_note": "Ship in two boxes." }- The Client Note (printed on documents the customer receives) is
customer_noteon these dedicated operations, butclient_noteon create and update above. The production note (for your shop floor only) isproduction_notehere, butprod_noteon update. - Both work on drafts, published, and even deleted orders. Send
nullto clear a note. Leave the field out to leave it unchanged.
4. Publish
Section titled “4. Publish”/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/publish
curl -X PATCH https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/publish \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json" \ -H "X-Yoprint-Decimal-Format: exact-v1"Publishing is the web app’s Publish button: the draft becomes a real order, shows in lists and reports, and can take payments, approvals and comments.
- It checks four things, reported in
data.validation:has_customer,has_order_items,has_sellable_items(no line on a phased-out or deleted product, size or color), and that the pricing group hasn’t been removed (removed_pricing_group). - A failed check still answers HTTP
200, withdata.valid: false. Always checksuccessanddata.valid, not the status code. data.validation.unsellable_itemslists each unsellable line with itsreason. Replace or remove those lines and publish again.data.validation.removed_pricing_groupnames the removed group, or isnull. Choose another with thechange_pricing_groupaction, then publish again.
5. Ask the customer to approve
Section titled “5. Ask the customer to approve”/v2/api/store/{team_slug}/v2/sales_order/{hash_id}/approval
curl https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/approval \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -H "X-Yoprint-Decimal-Format: exact-v1" \ -d '{ "notes_to_customer": "Please review the sizes and colors.", "send_later": false }'- Creates an order approval on a published order and, by default, emails it. The customer opens it, types their name as a signature, and approves it or asks for changes.
send_later: true, or an order with no customer, creates the approval without emailing it: itsstatusstayspending. Send it later with thesend_approval_reminderaction or from the web app.
List an order’s approvals:
GET List the order approvals of an order/v2/api/store/{team_slug}/v2/sales_order/{hash_id}/approval
Record the customer’s answer yourself, for example when they answered by phone:
PUT Record the customer's answer to an order approval/v2/api/store/{team_slug}/v2/sales_order/{hash_id}/approval/{hash_id2}/complete
{ "signature": "Alex Rivera", "status": "completed" }Send status: "need-changes" with notes_from_customer to record a change request instead. Always send status: leaving it out still completes the approval, with no status set.
6. Convert or clone
Section titled “6. Convert or clone”Converting turns the record itself into the other kind, keeping its id:
/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/action/{action_name}
curl -X PUT https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/action/convert_to_sales_order \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json" \ -H "X-Yoprint-Decimal-Format: exact-v1" \ -H "Content-Type: application/json" \ -d '{}'- Run
convert_to_sales_orderon a quote, orconvert_to_quoteon a sales order. Both take an optionalstatus_id(a status of the target kind, by default the team’s starting status) and an optionalpricing_group_id. - Converting a sales order back to a quote releases its stock reservations.
- Shopify rule: a Shopify order, or an order whose customer is a Shopify customer, can’t be converted, in either direction. Neither can a deleted order. Restore it first.
Cloning makes a copy instead, as a new draft with a new number and the starting status:
{ "is_time_shift": false }is_time_shift: trueshifts every date so the copy’s issue date iscloned_anchor_date.falsekeeps the original dates.item_choicesis required for each line on a phased-out or deleted product, size or color: leave it out, or replace it with another size or color of the same product.- Shopify orders can’t be cloned.
7. Delete and restore
Section titled “7. Delete and restore”/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}
- Deleting an order that’s already deleted succeeds and changes nothing.
Bring it back, with everything deleted alongside it:
PATCH Restore a deleted sales order or quote/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/restore
- Restoring an order that isn’t deleted answers HTTP
200withsuccess: false. Checksuccess, not the status code, before treating it as done.
8. Read the history
Section titled “8. Read the history”/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/history
Returns who changed what and when, newest first, a page at a time (pagination.total, perPage, currentPage).
{ "action": "status_change", "message_key": "order.status_change" }- Works on deleted orders too.
- Use this, not the retired v1 history.
Pitfalls
Section titled “Pitfalls”hide_drafton the order search doesn’t hide drafts, despite its name. Checkinternal_statusin the results instead.- Order approvals treat some orders specially. Listing them returns an empty list for a Shopify order, and
404for an old (version 1) order, the same as for an unknown one. Asking for one on a Shopify order is refused with422. - Clone and the two conversions keep the order’s own pricing group. If it was removed, they’re refused with
clone_needs_pricing_group. The refusal’serror.suggested_pricing_groupnames one to send instead. - An unknown
customer_iddoesn’t fail the create. It makes a draft with no customer, and publishing fails later. A customer synced from Shopify is refused with422. - Amounts are decimal pairs or thousandths depending on the field, and
deposit_totalinchange_payment_termis a percentage sent as a pair withdpexactly3. See Money and quantities. - Creates aren’t idempotent. If a create times out, search before you retry. See Retries and duplicates.
