Create an invoice for a sales order
AI agents: read /llms.txt; add .md to any page URL. This page as Markdown: /reference/invoices/v2-sales-order-invoice-create.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.
POST/
Creates an invoice for an order, optionally with its lines in the same call.
The invoice starts with the order's billing and shipping address and issue date unless you send billing_address_id, shipping_address_id or issue_date. It gets the team's next invoice number.
Send {} for an empty invoice. Lines go in item_actions, in the same shape as the invoice line bulk operation: each entry has your bid, an action (create, update or delete), cart_item_id for updates and deletes, and a payload. Without item_actions the invoice is created empty. tax_selection (with both expected_tax and desired_tax) sets the invoice's tax as Set the tax of an invoice does; resort_items: true re-sorts the lines.
Send the header X-Yoprint-Decimal-Format: exact-v1. Without it, a save that would return an amount the old number format can't carry is refused with 422 amount_out_of_range.
The answer is the invoice (with its lines when you sent item_actions, tax_selection or resort_items). {unique_key} is the order's id (for example V4keXwwxMQyXw7) or its unique_key (ord_…).
Business rules
- Refused for Shopify orders: "Cannot create invoice for a Shopify-synced order."
- Refused when the order keeps its invoice up to date automatically: "You cannot create an invoice for an auto managed order." Turn that off with the
configure_order_auto_invoice_managedaction first. - When
tax_selectionis sent it must contain bothexpected_taxanddesired_tax(either may be null).
Side effects
- Creates the invoice and its lines and refreshes the order for people who have it open in the web app; on a published sales order, Customer Portal pages the customer has open refresh too. Nothing is emailed.
- With
item_actions, recalculates the order's invoiced totals and spreads its payments across the invoices again (money of a payment frozen by an open dispute or review stays where it is).
Path parameters
Your team slug, shown as Team ID next to your API key in Profile.
- Example
your-team
- Example
The sales order's or quote's
id(a hash id), or itsunique_key(ord_…). The order number (scoped_id) does not work.- Example
V4keXwwxMQyXw7
- Example
Request body
application/json, required.
billing_address_idstring | nullThe billing address id. Default: the order's.
issue_datestring (date)The invoice date (ISO 8601). Default: the order's issue date.
item_actionsarray of objectsInvoice line operations, each with your own
bid:createwithpayload;updatewithcart_item_idandpayload;deletewithcart_item_id.Fields of each item (4)
- One of:
"update","create","delete"
- One of:
cart_item_idstringFor
updateanddelete: the invoice line's id.payloadobjectRequired for
createandupdate: one invoice line, the same body as Add a line to an invoice. Three common shapes. A sales order line:{ "product": { "type": "order_item", "properties": { "id": "<order line id>" } }, "quantity": { "type": "unit", "total": { "value": 10, "dp": 0 } }, "tax": { "type": "taxable" }, "position": 100 }. A shipping fee:{ "product": { "type": "shipment_fee", "properties": { "name": "Shipping" } }, "pricing": { "type": "fee", "product": { "total": { "value": 1500, "dp": 2 } } }, "position": 200 }. A percent discount:{ "product": { "type": "invoice_discount", "properties": { "type": "percent", "rate": { "value": 10, "dp": 0 }, "applies_to_shipping": false } }, "position": 300 }. Tax lines go intax_selection, not here.
resort_itemsbooleantrue to re-sort the invoice's lines.
shipping_address_idstring | nullThe shipping address id. Default: the order's.
tax_selectionobjectThe invoice's tax: send both
expected_tax(the tax line you expect now, or null) anddesired_tax(the tax you want, or null).Child fields (2)
desired_taxobject | nullChild fields (2)
propertiesobjectThe tax's settings: for
user_tax,{ "id": tax preset id }.typestring- One of:
"auto_tax","user_tax","override_tax"
- One of:
expected_taxobject | nullChild fields (2)
idstringThe current tax line's id.
lock_versioninteger- Minimum 0
Response
200Successful response.
An invoice.
Child fields (22)
Invoice total. A decimal pair: the amount is
value÷ 10^dp({ "value": 125000, "dp": 4 }is 12.50). Kept with 4 decimal places; an amount never set can come as{ "value": 0, "dp": 0 }. Readdpevery time: it can differ between fields and records.- See Money and quantities.
auto_invoice_managedbooleanbilling_address_idstring | nullcalc_deposit_totalinteger | number | string | nullThe order's deposit amount. Thousandths, an older encoding:
12500is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5is 1.2345), or as a string ("1234.5") when you sendX-Yoprint-Decimal-Format: exact-v1. See Money and quantities.cart_item_subtotaldecimal pairLines subtotal. A decimal pair: the amount is
value÷ 10^dp({ "value": 125000, "dp": 4 }is 12.50). Kept with 4 decimal places; an amount never set can come as{ "value": 0, "dp": 0 }. Readdpevery time: it can differ between fields and records.- See Money and quantities.
created_atstring (date-time) | nullCreated. ISO 8601 with offset, or null.
customerobject | nullThe customer record, with the same fields as Get a customer (
GET /v1/api/store/{team_slug}/customer/{hash_id}); the main ones are listed here.Child fields (6)
default_payment_term_idstring | nulldefault_pricing_group_idstring | nullidstringis_externalbooleantrue for a customer synced from an integration.
namestringtax_exemptboolean
discount_subtotaldecimal pairDiscount. A decimal pair: the amount is
value÷ 10^dp({ "value": 125000, "dp": 4 }is 12.50). Kept with 4 decimal places; an amount never set can come as{ "value": 0, "dp": 0 }. Readdpevery time: it can differ between fields and records.- See Money and quantities.
is_paidbooleanis_validbooleanissue_datestring (date-time) | nullIssue date. ISO 8601 with offset, or null.
itemsarray of objectsThe invoice lines.
order_scoped_idstringpaid_amountinteger | number | string | nullPaid on this invoice. Thousandths, an older encoding:
12500is 12.50. Usually a whole number; an amount finer than a thousandth comes back as a number with a fraction (1234.5is 1.2345), or as a string ("1234.5") when you sendX-Yoprint-Decimal-Format: exact-v1. See Money and quantities.pdf_urlarray of objectsThe invoice PDF links, despite the name: a list, first the standard invoice, then one per invoice PDF template the team has turned on.
Fields of each item (4)
descriptionstringlabelstringobject_typestringurlstringLink to the PDF.
scoped_idstring | integerThe invoice number, from the team's invoice counter. A new invoice created without lines answers it as a number (
6); with lines, and in every later read, it is a string ("6"). Compare it as text.shipment_subtotaldecimal pairShipping. A decimal pair: the amount is
value÷ 10^dp({ "value": 125000, "dp": 4 }is 12.50). Kept with 4 decimal places; an amount never set can come as{ "value": 0, "dp": 0 }. Readdpevery time: it can differ between fields and records.- See Money and quantities.
shipment_tax_subtotaldecimal pairTax on shipping. A decimal pair: the amount is
value÷ 10^dp({ "value": 125000, "dp": 4 }is 12.50). Kept with 4 decimal places; an amount never set can come as{ "value": 0, "dp": 0 }. Readdpevery time: it can differ between fields and records.- See Money and quantities.
shipping_address_idstring | nulltax_subtotaldecimal pairTax. A decimal pair: the amount is
value÷ 10^dp({ "value": 125000, "dp": 4 }is 12.50). Kept with 4 decimal places; an amount never set can come as{ "value": 0, "dp": 0 }. Readdpevery time: it can differ between fields and records.- See Money and quantities.
Errors
401The API key is missing or wrong (
unauthenticated).403You are not allowed to do this.
api_access_not_in_plan: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only{"message": "Insufficient Permission"}. Other refusals use the error envelope, withunauthorizedoruser_is_not_authorized.404Not found.
page_not_found: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's.code: null, with a message that starts "No query results for": there is no record with that id.422The request was refused and changed nothing.
validation_failedlists what to fix inerror.fields; other codes name a business rule, explained inerror.message. A few checks answer with only{"message": "..."}. An operation that calls another service (a payment provider, a carrier, an accounting app) can answerconnect_timeout("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.- When
- A Shopify or auto-invoiced order, or an invalid line or tax selection.
- Fix
- Read
error.message; fix the named line by itsbid.
Related
- POSTAdd, change and remove invoice lines at once
/v2/ api/ store/ {team_slug}/ v2/ invoice/ {unique_key}/ item/ bulk - PUTChange the tax on an invoice
/v2/ api/ store/ {team_slug}/ v2/ invoice/ {unique_key}/ tax-selection - GETGet an invoice
/v2/ api/ store/ {team_slug}/ v2/ invoice/ {unique_key} - PUTRun an action on a sales order or quote
/v2/ api/ store/ {team_slug}/ v2/ sales_order/ {order_unique_key}/ action/ {action_name}
