Appearance
Pagination, Sorting, Filtering & Consistency
This page standardizes pagination and query behavior across services. When a service has exceptions, it is documented on that service’s surface page.
Pagination (baseline)
- Default
limit: 8 - Clamp: 1–256 inclusive
- Cursor:
next_token(opaque, per-service format) - Behavior: if more data exists,
next_tokenis returned; if not, it isnullor omitted.
Clients must treat next_token as opaque and echo it back unchanged.
Sorting (baseline)
Stable sorting guarantees are per-endpoint. If a service does not document the order:
- Assume a deterministic order per cursor, but do not rely on a specific sort key.
- Do not mix filters without re-reading the first page.
Filtering & scoping (baseline)
- High-cardinality lists must include a scoping filter (org, facility, channel, parent ID, status).
- If a list endpoint allows
statusfiltering and includes a terminal state (for exampledoomed/archived), the default is the non-terminal state (active/current) unless the service documents otherwise. include_doomeddefaults to false unless explicitly set;status=all(where supported) orinclude_doomed=truereturns terminal records.
Search endpoints (exact vs partial)
Search behavior varies by service:
- Exact-match: identifiers, codes, and IDs are typically exact-match and tenant-scoped.
- Partial-match: supported only where documented (e.g., PMC product search, PVM list
textfilters). - Indexing latency: search planes can be eventually consistent. Expect transient staleness after writes (tokenized index or search-backed lists).
Target posture (UNCONFIRMED):
- Indexing latency p95 under 2 minutes for search planes.
- Explicit
staleflag returned when a query may be stale (future).
Tie-breakers (baseline)
When two results are equal on the primary sort key:
- Use a deterministic tie-breaker (e.g., record ID) to keep stable pagination.
- If not documented, treat ordering as stable but opaque.
Consistency expectations
- Read-after-write: strong consistency is not guaranteed across all services.
- List/search endpoints: may be eventually consistent under load.
- Search plane: always eventually consistent; use exact-match lookups for immediate consistency when needed.
Pagination Walkthrough
This walkthrough uses PVM GET /division as the example list endpoint. The same pattern applies to every list endpoint across all services.
curl (page-by-page)
First page (no next_token):
sh
curl -sS -G "https://api.g3nretailstack.com/pvm/division" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "limit=2"Response:
json
{
"success": true,
"data": {
"items": [
{ "division_id": "d1", "code": "APPAREL", "caption": "Apparel", "status": "active" },
{ "division_id": "d2", "code": "FOOTWEAR", "caption": "Footwear", "status": "active" }
],
"next_token": { "GSI1PK": "ORG#ACME", "GSI1SK": "DIV#d2" }
}
}Second page — echo next_token exactly as received:
sh
curl -sS -G "https://api.g3nretailstack.com/pvm/division" \
-H "x-orgcode: $ORGCODE" \
-H "x-session-guid: $SESSION_GUID" \
--data-urlencode "limit=2" \
--data-urlencode 'next_token={"GSI1PK":"ORG#ACME","GSI1SK":"DIV#d2"}'Response (last page — next_token is null):
json
{
"success": true,
"data": {
"items": [
{ "division_id": "d3", "code": "ACCESSORIES", "caption": "Accessories", "status": "active" }
],
"next_token": null
}
}Node.js (drain all pages)
js
async function listAll(path, params, headers) {
const items = [];
let nextToken = null;
do {
const url = new URL(`https://api.g3nretailstack.com${path}`);
Object.entries(params).forEach(([k, v]) => {
if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
});
if (nextToken) url.searchParams.set('next_token', JSON.stringify(nextToken));
const res = await fetch(url, { headers });
const json = await res.json();
if (!json.success) throw new Error(JSON.stringify(json.error));
items.push(...(json.data.items || []));
nextToken = json.data.next_token ?? null;
} while (nextToken);
return items;
}
// Usage
const divisions = await listAll('/pvm/division', { limit: 50, status: 'active' }, {
'x-orgcode': ORGCODE,
'x-session-guid': SESSION_GUID,
});Python (drain all pages)
python
import json
import requests
def list_all(path, params, headers):
items = []
next_token = None
while True:
query = {**params}
if next_token is not None:
query['next_token'] = json.dumps(next_token)
resp = requests.get(f"https://api.g3nretailstack.com{path}",
params=query, headers=headers)
data = resp.json()
if not data.get('success'):
raise RuntimeError(data.get('error'))
items.extend(data['data'].get('items', []))
next_token = data['data'].get('next_token')
if not next_token:
break
return items
# Usage
divisions = list_all('/pvm/division', {'limit': 50, 'status': 'active'}, {
'x-orgcode': ORGCODE,
'x-session-guid': SESSION_GUID,
})Best practices
- Prefer
getby ID for immediate consistency. - For lists, handle empty pages and retry with the same
next_token. - When in doubt, cross-check with a
getcall before acting on list/search results.