Filtering and sorting
AI agents: read /llms.txt; add .md to any page URL. This page as Markdown: /concepts/filtering-and-sorting.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.
To find records that match conditions, use a resource’s filter operation. Filters use POST because they carry a body, but they only read.
curl https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/filter \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json" \ -H "X-Yoprint-Decimal-Format: exact-v1" \ -H "Content-Type: application/json" \ -d '{ "filters": [ { "key": "is_rush", "operator": "eq", "value": true }, { "key": "due_date", "operator": "lte", "value": 7 } ], "sort": [{ "key": "due_date", "direction": "asc" }] }'This returns rush sales orders due before the 7th day from today starts in UTC (overdue ones included), soonest first, one page at a time. Date values count UTC days, and v1 filters count them differently. See Dates.
The body
Section titled “The body”| Field | Meaning |
|---|---|
filters |
A list of conditions. Each has a key, an operator and a value. A record must match every condition. |
sort |
A list of sort objects, each with a key and direction (asc or desc). |
page |
The page number to return. See Pagination. |
Where to find filter and sort keys
Section titled “Where to find filter and sort keys”Each resource allows filtering and sorting only on specific supported fields. To find the exact keys and allowed operators for a resource:
- Open that resource’s filter operation in the API reference, such as List sales orders, Search customers with filters or Filter jobs.
- The operation’s reference page lists every accepted filter
key, its value type, its supported operators, and every acceptedsortkey. - Sending an unlisted or misspelled key, or using an operator not supported by that field’s type, is refused with HTTP
422.
Operators by field type
Section titled “Operators by field type”The operators a condition can use depend on the type of its key:
| Field type | value |
Operators |
|---|---|---|
Boolean, such as is_rush |
true or false |
eq, neq |
Date, such as due_date |
A whole number of days from today, not a date | eq, gte, lte |
| Number or amount | A number | eq, gte, lte |
| Reference to other records, such as statuses, tags, customers or users | A list of hash ids | in, nin (not in) |
Single option, such as payment_status (paid or unpaid) |
A string | eq |
| Multiple option | A list of strings | in, nin |
A condition with a missing or null value, an unknown key or an operator its type doesn’t allow is refused with 422.
Dates are days from today, and v1 and v2 count differently
Section titled “Dates are days from today, and v1 and v2 count differently”A date condition’s value is a whole number of days from today: 0 is today, 7 is a week from today and -30 is 30 days ago. A date such as "2026-11-12" isn’t accepted: it’s refused with 422. Days are UTC days, not your shop’s days. The two filter versions also count from different points, and v1 counts lte backwards:
| Filters | Count from | lte N matches |
|---|---|---|
| v2: sales orders, quotes and invoices | 00:00 UTC at the start of day N | Everything before day N starts. Day N itself is left out. |
| v1: customers, jobs, tasks, shipments, packages, approvals and integrations | Now | On or before now minus N days |
| The production control calendar (a v1 path that counts forward, like v2) | Today’s UTC date | On or before the date N days from today, counting the whole day |
- v2 · lte 7Everything before the 7th day from today starts, overdue included
- v1 · lte 7Only dates 7 or more days ago
- v1 · lte -7Everything up to 7 days from now: in v1, a negative number reaches forward
v2 dates, exactly
Section titled “v2 dates, exactly”YoPrint turns N into a date, today’s UTC date plus N days, and compares the record’s date and time with 00:00 UTC on that day:
| Operator | Matches |
|---|---|
gte N |
From 00:00 UTC on day N onwards. |
lte N |
Up to 00:00 UTC on day N. Day N itself is left out: send N + 1 to include it. |
eq N |
Only a date stamped exactly 00:00:00 UTC on day N. It matches almost nothing, so use a gte and lte pair instead. |
Dates the web app sets are stored as the end of the shop’s day, in UTC. A due date of 12 November in New York is stored as 2026-11-13T04:59:59Z, on the next UTC day. So a UTC day and your shop’s day don’t line up, and a filter can’t pick your shop’s days exactly. Ask for a slightly wider range, then keep the right rows in your code:
- Your shop’s days A to B (in days from today): ask
gte A-1andlte B+2, then keep the rows whose date falls on those days in your shop’s time zone. The extra day at the start covers time zones ahead of UTC. The two extra days at the end cover time zones behind UTC and the left-out day N. - Overdue (the date is before now): ask
lte 1, then keep the rows whose date is before now.
Worked example: due today and tomorrow in New York
Section titled “Worked example: due today and tomorrow in New York”Today and tomorrow are days 0 and 1. Ask for gte -1 and lte 3, then keep the orders whose due_date, read in New York time, is today or tomorrow. With the yoprint helper from Calling the API:
const zone = 'America/New_York';// A date as YYYY-MM-DD, on the shop's calendar.const shopDay = (date) => new Intl.DateTimeFormat('en-CA', { timeZone: zone }).format(date);
const today = shopDay(new Date());const [y, m, d] = today.split('-').map(Number);const tomorrow = new Date(Date.UTC(y, m - 1, d + 1)).toISOString().slice(0, 10);
const page = await yoprint('POST', '/v2/api/store/{team_slug}/v2/sales_order/filter', { filters: [ { key: 'due_date', operator: 'gte', value: -1 }, { key: 'due_date', operator: 'lte', value: 3 }, ], sort: [{ key: 'due_date', direction: 'asc' }],});
const dueTodayOrTomorrow = page.data.filter( (order) => order.due_date && [today, tomorrow].includes(shopDay(new Date(order.due_date))),);Read every page of the answer the same way. See Pagination.
Worked out for both versions
Section titled “Worked out for both versions”| You want | v2 filters | v1 filters |
|---|---|---|
| Due from today through the 7th day from now | gte -1 and lte 9, then keep your shop’s days in code |
gte 0 and lte -7 (now to 7 days from now) |
| Overdue | lte 1, then keep the dates before now |
lte 0 (before now) |
| Created in the last 30 days | gte -30 (from 00:00 UTC 30 days ago) |
gte -30 (from now minus 30 days) |
v1 date operators, exactly
| Operator | v1: matches a date… |
|---|---|
gte N |
on or after now + N days |
lte N |
on or before now − N days (counts backwards) |
eq N |
anywhere in UTC day today + N |
The invoice filter’s order dates count whole days. On the v2 invoice filter, the keys that read the invoice’s order (order_issue_date, full_amount_due, deposit_due, order_cust_due_date and order_ship_due_date) compare whole UTC days. lte N includes all of day N, and eq 0 matches anywhere in today’s UTC day. The invoice’s own created_at follows the v2 rule above. Asking for a wider range and narrowing it in your code works here too.
Statuses, tags and other references
Section titled “Statuses, tags and other references”References take a list of hash ids, even for one value:
{ "key": "status_id", "operator": "in", "value": ["BkzhmfgkK2HFTs", "ZXhN3KtjHgQZFz"] }Get status ids from the status lists, such as:
GET List sales order statuses/v1/api/store/{team_slug}/setting/status/sales_order
Sorting
Section titled “Sorting”The sort array controls the order of returned records. Each sort item is an object with a field key and a direction:
{ "sort": [ { "key": "created_at", "direction": "desc" } ]}direction:"asc"for ascending (earliest date first, lowest number first, A to Z) or"desc"for descending (latest date first, highest number first, Z to A).- v2 filters (sales orders, quotes, invoices): apply only the first entry in
sort. Any subsequent entries in the array are ignored. - v1 filters (jobs, customers, shipments and others): apply every entry in order, sorting by the first key and breaking ties with the second.
- Default order: on a v2 filter, an omitted or empty
sortuses the resource’s default order (typicallycreated_atdescending). v1 filters have no default order: sendsortwhenever the order matters. - Pagination stability: sort by a field that doesn’t change, such as
created_at. See Pagination. - Allowed keys: You can only sort by keys listed on that resource’s filter reference page. Unrecognized sort keys are refused with
422.
Comparing v1 and v2 filters
Section titled “Comparing v1 and v2 filters”| Feature | v1 filters | v2 filters |
|---|---|---|
| Applicable resources | Customers, jobs, tasks, shipments, packages, approvals, integrations | Sales orders, quotes, invoices |
Empty filters |
Refused (422). Send at least one condition. |
Allowed: returns every record, a page at a time. |
| Sort entries used | All entries in order (multi-field sort) | The first entry only (single-field sort) |
lte date operator |
Counts backwards into the past (now - N) |
Counts forwards into the future (start of day + N) |
| Count only | No | "count_only": true returns the pagination block without records. |
Search instead of filter
Section titled “Search instead of filter”To find an order from a few words, such as its number, its name or the customer’s name, use search:
GET Search sales orders and quotes/v2/api/store/{team_slug}/search/orders
It takes q, and optionally customer_id and quote (1 for quotes only, 0 for sales orders only, or leave it out for both). true or false for quote is refused with 422. It returns up to 30 matches.
Choose what comes back
Section titled “Choose what comes back”Many reads accept with, a list of related records to include, and only, a list of fields to return. Use them where an operation’s reference page documents them. The v2 filters accept only some related records.
