Money and quantities
AI agents: read /llms.txt; add .md to any page URL. This page as Markdown: /concepts/money-and-quantities.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.
YoPrint sends and takes money and quantities as exact values, not floating-point numbers. Different fields use different encodings, listed below. The exception is a Shippo rate request, which takes plain numbers. Before you read or send an amount, check its encoding here or on its reference page.
At a glance
Section titled “At a glance”- Read
dpevery time you read a decimal pair. The scale is not fixed:dpcan differ between fields and even between records of the same field. - Send
X-Yoprint-Decimal-Format: exact-v1on every request. This header ensures amounts that cannot be represented as exact JSON numbers return as strings. - Use a decimal type for arithmetic. Never use native binary floating-point numbers for money or quantities.
- Prefer decimal-pair fields over deprecated thousandths fields. Where an older
calc_*field has a decimal-pair replacement, read the replacement instead. - Check each field’s reference entry for its encoding. Fields use different encodings across answers and requests.
The encodings
Section titled “The encodings”| Encoding | Looks like | Means | Used for |
|---|---|---|---|
| Decimal pair | { "value": 1250, "dp": 2 } |
12.50: value ÷ 10^dp |
Newer amounts: line prices, costs and quantities, order and invoice totals |
| Thousandths | 12500 |
12.50: ÷ 1,000 | Older amounts: payments, customer balances, product prices, and the deprecated calc_* totals |
| Percentage × 1000 | 8250 |
8.25%: ÷ 1,000 | Tax rates and deposit percentages |
| Ten-thousandths of a percent | 82500 |
8.25%: ÷ 10,000 | The automatic sales tax lookup’s rate |
| Decimal string | "24.00000000", "12.40" |
The number as written | Stock levels, Shippo rates and weights, some report rows |
| Plain number | 12.5 |
The number as written | Shippo rate requests only |
| Formatted string | "$125.00" |
Text for people | The payment webhook’s amount: don’t parse it |
Every field, by encoding
- Decimal pair (requests and answers): line item prices, costs, and quantities. Order totals (
order_item_cart_total,invoiced_total,order_balance_due,deposit_amount_due,shipment_cost_total, margin, markup). Invoice totals (total,cart_item_subtotal,tax_subtotal, …) and line items. - Thousandths (requests and answers), older fields:
- Order amounts:
paid_amountandcalc_deposit_total. - Customer balances:
unpaid_amountandwallet_fund. - Product prices:
default_price. - Payments and refunds:
amount, sent and answered. - v1 filter amounts, and package label
charges. - The deprecated
calc_*totals on orders and invoices.
- Order amounts:
- Percentage × 1000 (requests and answers): tax preset rates, payment term
deposit_percent, a shipping method’sprice_percent, and an order’sdeposit_totalin answers (sent as a pair withdp3). - Ten-thousandths of a percent (answers): the automatic sales tax lookup’s
total(the combined rate). - Decimal string, stock (answers): stock levels (
stock_on_hand,stock_incoming,qty_need_ordering), with 8 decimal places, or"0". - Decimal string, money and weights (answers): Shippo rates and package weights (
amount,weight). Some report rows (purchase order reportcalc_*, tax report amounts). - Plain number (requests): Shippo rate requests: parcel dimensions, parcel weights, and insurance amount (Shippo’s own format, passed through).
- Formatted string (answers): the payment webhook’s
amount, in the team’s currency. Read the order or invoice for exact amounts.
Encodings in transition
Section titled “Encodings in transition”YoPrint is moving amounts to decimal pairs:
- BeforeAmounts in thousandths, such as an order’s
calc_total - NowDecimal pairs added. The old
calc_*copies still come back, marked deprecated, so existing integrations keep working - LaterThe deprecated copies are removed. Thousandths fields without a pair yet may change too
See Deprecated fields for each old field and the one to read instead.
Reading amounts
Section titled “Reading amounts”Decimal pairs
Section titled “Decimal pairs”- value
1250: the digits, with no decimal point - dp
2: how many of those digits come after the point - The amount1250 ÷ 10² = 12.50
A decimal pair consists of an integer value and a scale dp. The amount is value ÷ 10^dp:
{ "value": 1250, "dp": 2 }is 1250 ÷ 10² = 12.50.{ "value": 125, "dp": 1 }is 125 ÷ 10¹ = 12.5.{ "value": 3, "dp": 0 }is 3 (a quantity of three).
When you read a pair, never assume dp. Answers keep the scale at which the amount is stored in YoPrint. The same field can return with different scales across different records, and an unset amount can return as { "value": 0, "dp": 0 }. Always read dp directly from the object.
| Kind of field | Typical dp |
Example |
|---|---|---|
| Order and invoice totals | 4 | { "value": 1250000, "dp": 4 } (125.00) |
| Line item unit prices and costs | 6 | { "value": 12500000, "dp": 6 } (12.50) |
| Line item quantities | 0 | { "value": 12, "dp": 0 } (12 items) |
| Unset amounts | 0 | { "value": 0, "dp": 0 } (0.00) |
Thousandths
Section titled “Thousandths”- The field
12500 - 1,000The same for every thousandths field
- The amount12.50
A thousandths field represents an amount multiplied by 1000. The amount is value ÷ 1000:
12500is 12500 ÷ 1000 = 12.50.125000is 125000 ÷ 1000 = 125.00.
In answers, the value is usually a whole number. When an amount is finer than a thousandth, it returns as a fractional number: 1234.5 is 1234.5 ÷ 1000 = 1.2345. When you send X-Yoprint-Decimal-Format: exact-v1, any fractional thousandths value (or any value too large for a JSON number) returns as an exact decimal string of thousandths, such as "1234.5".
Percentages × 1000
Section titled “Percentages × 1000”A percentage × 1000 represents a percentage multiplied by 1000: 8250 is 8250 ÷ 1000 = 8.25%. With X-Yoprint-Decimal-Format: exact-v1, any value that is not a whole number returns as an exact string.
Decimal strings
Section titled “Decimal strings”Stock levels return as strings with 8 decimal places (such as "24.00000000"), and Shippo rates return as decimal strings (such as "12.40"). Always parse them into your language’s decimal type.
Formatted strings
Section titled “Formatted strings”Payment webhooks include an amount field formatted as a currency string (such as "$125.00"). This string is intended for display to people, not for calculations. Do not parse it: currency symbols, digit separators, and decimal conventions vary by team currency. To get exact amounts, fetch the order or invoice from the API.
Sending amounts
Section titled “Sending amounts”Most mistakes with money happen when sending it. Find the field you send in this table, and send it in that unit. Each example is 12.50 (or 8.25% for a rate).
| You send | Fields | Unit | Example |
|---|---|---|---|
| Prices, costs and quantities on order, invoice and shipment lines | pricing.…total, quantity.total |
Decimal pair | { "value": 1250, "dp": 2 } |
| A payment, a refund, or a card refund through the gateway | amount |
Thousandths | 12500 |
| Customer credit: add or take out, pay an order from it, move payments to it | amount |
Thousandths | 12500 |
| A payment link’s allocations | total |
A pair where value counts thousandths, whatever dp says |
{ "value": 12500, "dp": 3 } |
| Product and variant prices | default_price, and price in price_data |
Thousandths | 12500 |
| Fee preset prices, a shipping method’s minimum price | price, min_price |
Thousandths | 12500 |
| Shipping box sizes | length, width, height |
Thousandths of the unit | 12500 is 12.5 |
| Tax preset rates, a payment term’s deposit, a shipping method’s markup | the rate fields, deposit_percent, price_percent |
Percentage × 1000 | 8250 |
| An order’s deposit percentage | deposit_total |
Percentage × 1000, in a pair with dp exactly 3 |
{ "value": 8250, "dp": 3 } |
| Shippo rate requests | parcel sizes and weights, insurance amount |
Plain number, as Shippo takes it | 12.5 |
When a field isn’t in this table, its reference page says its unit.
Decimal pairs
Section titled “Decimal pairs”When sending a decimal pair in a request body:
valuemust be an integer, or a string of digits with an optional leading-. No decimal point and no leading zeros.dpmust be an integer from 0 to 30.- A plain number such as
12.5, or a pair with a fractional number such as{ "value": 12.5, "dp": 1 }, is refused with HTTP422. - YoPrint limits the maximum scale and value by field type:
| Kind of field | Most decimal places | Largest value |
|---|---|---|
| Quantities | 8 | 999,999,999,999.99999999 |
| Unit prices, costs, and rates | 6 | 999,999,999,999,999.999999 |
| Calculated amounts (discounts, totals) | 4 | 999,999,999,999,999.9999 |
Sending more decimal places than allowed is refused with HTTP 422, unless the trailing digits are zeros.
Thousandths
Section titled “Thousandths”Send a whole number of thousandths, with no decimal point: 25000 is 25.00. What happens to a number with decimals depends on the field:
- A payment or refund
amount, and every customer creditamount(adding or taking out credit, paying an order from it, moving payments to it): refused with422validation_failed, and nothing is recorded.error.fields.amountsays “The amount must be a whole number of thousandths of the currency unit, such as 12500 for 12.50.” - Product and variant prices: the request fails with HTTP
500. Fix the number before you send it again.
The sign of a payment or refund amount is ignored: -25000 also records 25.00.
Request quirks
Section titled “Request quirks”Ask for exact responses
Section titled “Ask for exact responses”Send this header on every request:
X-Yoprint-Decimal-Format: exact-v1With this header:
- A decimal pair whose
valueis too large for a JSON number to hold exactly (beyond 9,007,199,254,740,991, which is 2^53 − 1) returns withvalueas a string of digits. - A thousandths field that is not a whole number returns as an exact decimal string of thousandths, such as
"1234.5". - A percentage × 1000 that is not a whole number returns as an exact decimal string.
Without it, a save of an order, an invoice, a shipment or their lines is refused with 422 amount_out_of_range when its answer would carry an amount the older number format can’t hold exactly. Nothing is saved: send the header and try again.
Both v1 and v2 accept this header, and YoPrint echoes X-Yoprint-Decimal-Format: exact-v1 in the response when it applied it. Always parse value and thousandths fields as either a number or a string. The client helpers on Calling the API send this header on every request.
Which order total to read
Section titled “Which order total to read”What each order total counts, and how the balance is worked out, is on How the balance works. Its fields are decimal pairs, except paid_amount, which is in thousandths.
Convert without rounding
Section titled “Convert without rounding”Keep amounts as integers or strings until you display them. Do not convert to a binary floating-point number and back.
// "4017.86" from { value: 40178600, dp: 4 }function pairToString({ value, dp }) { const digits = String(value).replace('-', '').padStart(dp + 1, '0'); const sign = String(value).startsWith('-') ? '-' : ''; const whole = digits.slice(0, digits.length - dp); const fraction = digits.slice(digits.length - dp).replace(/0+$/, ''); return sign + whole + (fraction ? '.' + fraction : '');}
// "125" from 125000, and "1.2345" from 1234.5 or "1234.5" (thousandths)function thousandthsToString(n) { const [whole, fraction = ''] = String(n).split('.'); return pairToString({ value: whole + fraction, dp: 3 + fraction.length });}Use your language’s decimal type for arithmetic: decimal.Decimal in Python, BCMath or brick/math in PHP, or a decimal library in JavaScript.
Currency
Section titled “Currency”Amounts are in the team’s currency: an order takes the team’s default currency when it’s made. No numeric encoding carries the currency. Only the formatted display strings in webhooks include a currency symbol.
