Skip to content

Create and change quotes and sales orders

Open .md

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. 1. Create a draftQuote or sales order, with defaults from the team and customer
  2. 2. Read and find ordersBy id, a filtered list, or a quick search
  3. 3. Change itHeader fields with update. Everything else with a named action
  4. 4. PublishCheck data.valid: a failed publish can still answer 200
  5. 5. Ask the customer to approveOnly once published
  6. 6. Convert or cloneConvert keeps the id. Clone makes a new draft
  7. 7. Delete and restoreDeleting also undoes money and stock
  8. 8. Read the historyWho changed what, and when
Each step has its own section below. Steps 5 and 6 don't apply to every order.
POST Create a sales order or quote /v2/api/store/{team_slug}/v2/sales_order/new
Terminal window
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_type is quote or sales_order. Send quote (or the older quote: true) for a quote. Leave both out for a sales order.
  • customer_id is 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 as SO1001): your own, if you send scoped_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.

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 customer record, 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
Terminal window
curl "https://secure.yoprint.com/v2/api/store/your-team/search/orders?q=northwind&quote=0" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json" \
-H "X-Yoprint-Decimal-Format: exact-v1"
  • q matches 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=1 searches quotes only, quote=0 sales orders only. For a POST body, send "quote": true or false instead.
  • For more than 30 results, or for filters, use the list operations above instead.

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_id and customer_contact_id, created_by, and status_id.
  • Its object_type and quote fields take only the order’s current value on a draft. They don’t convert it. Use the convert_to_quote or convert_to_sales_order action for that.
  • status_id here changes the status without sending the status webhooks (the change is still recorded in the history). Prefer the change_status action 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}
Terminal window
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_customer resets 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_date and send_order_detail work. Every other action fails with 422.

  • 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_note on these dedicated operations, but client_note on create and update above. The production note (for your shop floor only) is production_note here, but prod_note on update.
  • Both work on drafts, published, and even deleted orders. Send null to clear a note. Leave the field out to leave it unchanged.
PATCH Publish a draft sales order or quote /v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/publish
Terminal window
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, with data.valid: false. Always check success and data.valid, not the status code.
  • data.validation.unsellable_items lists each unsellable line with its reason. Replace or remove those lines and publish again.
  • data.validation.removed_pricing_group names the removed group, or is null. Choose another with the change_pricing_group action, then publish again.
POST Ask the customer to approve an order /v2/api/store/{team_slug}/v2/sales_order/{hash_id}/approval
Terminal window
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: its status stays pending. Send it later with the send_approval_reminder action 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.

Converting turns the record itself into the other kind, keeping its id:

PUT Run an action on a sales order or quote /v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/action/{action_name}
Terminal window
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_order on a quote, or convert_to_quote on a sales order. Both take an optional status_id (a status of the target kind, by default the team’s starting status) and an optional pricing_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: true shifts every date so the copy’s issue date is cloned_anchor_date. false keeps the original dates.
  • item_choices is 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.
DELETE Delete a sales order or quote /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 200 with success: false. Check success, not the status code, before treating it as done.
GET List the history of a sales order or quote /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.
  • hide_draft on the order search doesn’t hide drafts, despite its name. Check internal_status in the results instead.
  • Order approvals treat some orders specially. Listing them returns an empty list for a Shopify order, and 404 for an old (version 1) order, the same as for an unknown one. Asking for one on a Shopify order is refused with 422.
  • 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’s error.suggested_pricing_group names one to send instead.
  • An unknown customer_id doesn’t fail the create. It makes a draft with no customer, and publishing fails later. A customer synced from Shopify is refused with 422.
  • Amounts are decimal pairs or thousandths depending on the field, and deposit_total in change_payment_term is a percentage sent as a pair with dp exactly 3. See Money and quantities.
  • Creates aren’t idempotent. If a create times out, search before you retry. See Retries and duplicates.