Summary
The auto-generated list-action flag sets are misaligned with what the Zoho Inventory API actually accepts. The packages list endpoint is the trigger — but the same pattern is likely repeated across most resources, since they were all generated from the same gen-resources.mjs heuristics.
Symptom: agents calling <resource> list with what looks like a working filter (--status, --reference_number, --customer_name, --date, --salesorder_id, --salesorder_number) hit the CLI's "BROKEN — falls back to client-side filter on the full list" path, even though Zoho's real API supports filtering server-side via different param names. Result: every list call pulls the entire dataset and filters in memory. Slow, wasteful, and opaque to the agent.
Concrete reproduction — packages list
CLI flag set today (scripts/gen-resources.mjs, packages entry):
--reference_number, --package_number, --salesorder_id, --salesorder_number, --customer_id, --customer_name, --status, --date, --search_text → all marked brokenListFilters, with comment "Zoho docs list a filter set (... filter_by, …) but in practice the exact-match forms of package_number, salesorder_number, salesorder_id are silently ignored". The conclusion was extended to every filter, not just those three.
What the docs actually specify (https://www.zoho.com/inventory/api/v1/packages/#list-all-packages):
| Param |
Type |
Notes |
filter_by |
string |
The canonical status filter. Allowed values: All, NotShipped, Shipped, Delivered. Not exposed by the CLI at all. |
customer_id |
string |
Real exact-match filter — not broken. |
search_text |
string |
Real free-text search. |
sort_column |
string |
tracking_number / salesorder_number / package_number / date / created_time / last_modified_time / customer_name / customer_id / shipment_date / quantity / delivery_method. |
packing_number_startswith / packing_number_contains |
long |
The CLI exposes bare --package_number, which Zoho ignores. The startswith/contains forms work. |
salesorder_number_startswith / salesorder_number_contains |
long |
Same pattern — CLI exposes bare --salesorder_number, real API wants startswith/contains. |
customer_name_startswith / customer_name_contains |
string |
Same pattern. |
delivery_method_startswith / delivery_method_contains |
string |
Not exposed at all by CLI. |
date_start / date_end |
date |
CLI exposes bare --date, which Zoho ignores. |
shipment_date_start / shipment_date_end |
date |
Not exposed at all by CLI. |
Live verification (run today against org_id=60033513768, India DC):
# Total packages: 319
zoho-inventory-cli packages list --all | jq length
# → 319
# With filter_by=Status.Shipped via direct curl: 83 packages — server-side filter WORKS
curl -s -H "Authorization: Zoho-oauthtoken $ACCESS" \
"https://www.zohoapis.in/inventory/v1/packages?organization_id=$ORG&filter_by=Status.Shipped&per_page=200" \
| jq '.packages | length'
# → 83
So filter_by is the headline param the CLI should expose, and the assumption that "all list filters are broken" is wrong — only the exact-match equivalents of startswith/contains are broken because they don't exist in the API in that form.
Request — full pass across all resources
Audit every list action in scripts/gen-resources.mjs against the live Zoho Inventory v1 docs (https://www.zoho.com/inventory/api/v1/) and the live API. For each list endpoint:
- Read the docs for the supported query params, including the
filter_by enum and any *_startswith / *_contains / *_start / *_end variants.
- Probe each documented param against the live API to confirm it actually filters server-side (Zoho's docs aren't always honest, hence why this issue exists).
- Replace the resource entry in
gen-resources.mjs:
- Add a
listFilters array with the params that genuinely work server-side, including filter_by (with allowed-values doc-string surfaced in --help).
- Move the genuinely-broken bare-name shortcuts to
brokenListFilters only if they have ergonomic value (better to drop them entirely and force callers to use the real param names).
- Add
sort_column / sort_order flags where supported.
- Regenerate, lint, ensure the tests still pass, and update
knowledge/ with any per-resource gotchas discovered during probing.
Resources known or suspected to need the same fix: packages, shipment-orders, sales-orders, invoices, credit-notes, customer-payments, purchase-orders, bills, vendor-credits, delivery-challans, transfer-orders. Probably all of them.
Tests should cover at least one filter_by round-trip and one *_startswith round-trip per resource that has them.
Why this matters
Every misaligned flag adds wire payload + latency to scheduled jobs and skill runs. For the fix-coffee-delayed-shipments daily sweep at Fix Coffee, switching from packages list --all (319 records, 2.5s) to filter_by=Status.Shipped (83 records, ~0.7s) is a ~4× reduction. Across all resources with daily/weekly automations, the cumulative win is meaningful — and more importantly, the CLI stops teaching agents to ignore filters with its blanket "BROKEN" annotations.
Out of scope
- The genuinely-broken bare-name filters can stay annotated as such — but the comment should say "Zoho ignores this exact-match form; use
filter_by / *_startswith / *_contains instead", not the current blanket "all filters broken".
- Body-vs-query parameter routing on POST/PUT is a separate concern (
packages create already handles this correctly via queryFlags). Not part of this issue.
Summary
The auto-generated list-action flag sets are misaligned with what the Zoho Inventory API actually accepts. The
packages listendpoint is the trigger — but the same pattern is likely repeated across most resources, since they were all generated from the samegen-resources.mjsheuristics.Symptom: agents calling
<resource> listwith what looks like a working filter (--status,--reference_number,--customer_name,--date,--salesorder_id,--salesorder_number) hit the CLI's "BROKEN — falls back to client-side filter on the full list" path, even though Zoho's real API supports filtering server-side via different param names. Result: every list call pulls the entire dataset and filters in memory. Slow, wasteful, and opaque to the agent.Concrete reproduction —
packages listCLI flag set today (
scripts/gen-resources.mjs, packages entry):--reference_number,--package_number,--salesorder_id,--salesorder_number,--customer_id,--customer_name,--status,--date,--search_text→ all markedbrokenListFilters, with comment "Zoho docs list a filter set (... filter_by, …) but in practice the exact-match forms ofpackage_number,salesorder_number,salesorder_idare silently ignored". The conclusion was extended to every filter, not just those three.What the docs actually specify (https://www.zoho.com/inventory/api/v1/packages/#list-all-packages):
filter_byAll,NotShipped,Shipped,Delivered. Not exposed by the CLI at all.customer_idsearch_textsort_columntracking_number/salesorder_number/package_number/date/created_time/last_modified_time/customer_name/customer_id/shipment_date/quantity/delivery_method.packing_number_startswith/packing_number_contains--package_number, which Zoho ignores. The startswith/contains forms work.salesorder_number_startswith/salesorder_number_contains--salesorder_number, real API wants startswith/contains.customer_name_startswith/customer_name_containsdelivery_method_startswith/delivery_method_containsdate_start/date_end--date, which Zoho ignores.shipment_date_start/shipment_date_endLive verification (run today against
org_id=60033513768, India DC):So
filter_byis the headline param the CLI should expose, and the assumption that "all list filters are broken" is wrong — only the exact-match equivalents of startswith/contains are broken because they don't exist in the API in that form.Request — full pass across all resources
Audit every
listaction inscripts/gen-resources.mjsagainst the live Zoho Inventory v1 docs (https://www.zoho.com/inventory/api/v1/) and the live API. For each list endpoint:filter_byenum and any*_startswith/*_contains/*_start/*_endvariants.gen-resources.mjs:listFiltersarray with the params that genuinely work server-side, includingfilter_by(with allowed-values doc-string surfaced in--help).brokenListFiltersonly if they have ergonomic value (better to drop them entirely and force callers to use the real param names).sort_column/sort_orderflags where supported.knowledge/with any per-resource gotchas discovered during probing.Resources known or suspected to need the same fix:
packages,shipment-orders,sales-orders,invoices,credit-notes,customer-payments,purchase-orders,bills,vendor-credits,delivery-challans,transfer-orders. Probably all of them.Tests should cover at least one
filter_byround-trip and one*_startswithround-trip per resource that has them.Why this matters
Every misaligned flag adds wire payload + latency to scheduled jobs and skill runs. For the
fix-coffee-delayed-shipmentsdaily sweep at Fix Coffee, switching frompackages list --all(319 records, 2.5s) tofilter_by=Status.Shipped(83 records, ~0.7s) is a ~4× reduction. Across all resources with daily/weekly automations, the cumulative win is meaningful — and more importantly, the CLI stops teaching agents to ignore filters with its blanket "BROKEN" annotations.Out of scope
filter_by/*_startswith/*_containsinstead", not the current blanket "all filters broken".packages createalready handles this correctly viaqueryFlags). Not part of this issue.