Source of truth: AO_Base.pas (the mpt* constants), CM_Base.InitGetQueryParameters / InitPostGetQueryParameters / GetFieldListParam / AllowableField, CM_Inventory.InitGetQueryParameters, ServerMethodsBase.GetJSONFields / GetJSONFieldsAndFilters / IsValidOperator.
These parameters recur across many endpoints. Swagger lists them per operation but does not say how they behave, what happens when they are wrong, or how they interact with each other.
1. Query-string parameters
All of these are read from the query string, never from the body, and they are only honoured by endpoints that opt into them. An endpoint that does not declare a parameter will silently ignore it.
| Parameter | Type | Applies to |
|---|---|---|
Fields | comma-separated list | GET and POST field shaping |
DetailedResponse | Y / anything else | POST endpoints |
PageSize | integer | Paginated GETs |
PageNumber | integer | Paginated GETs |
EffectiveDateTime | ISO 8601 | "changes since" GETs |
MarkedDeleted | Y / anything else | Inventory GETs |
eCommerceExport | Y / anything else | Inventory GETs |
CurrencyCode | integer | Inventory GETs |
Department | integer | Inventory GETs |
FreeFormNameMap | Y / anything else | GETs that expose free-form fields |
PriceLevel | integer | Inventory GETs |
Note the exact spellings. EffectiveDateTime is not EffectiveDate, and FreeFormNameMap is not FreeFormMap, even though the internal Delphi constants are named mptEffectiveDate and mptFreeFormMap.
A shared failure mode: non-numeric integers throw
CurrencyCode, Department, PriceLevel, PageSize and PageNumber are parsed with StrToInt, not StrToIntDef. Passing a non-numeric value raises a Delphi conversion exception, which surfaces as HTTP 200 with a failure envelope containing text like:
'abc' is not a valid integer value
Send these as bare integers with no units, no thousands separators and no empty string. Omit the parameter entirely rather than sending it empty.
2. Fields — response field shaping
?Fields=PartNumber,Description,OnHand
Parsed by GetFieldListParam into a TStringList via its CommaText property, then applied by CM_Base.AllowableField: a field is emitted only if its name appears in the list.
Rules and gotchas:
- Comma-separated, in the query string. Because it is parsed as
CommaText, values containing spaces or commas must be double-quoted, e.g.Fields="Part Number",Description. Prefer field names without spaces. - Matching is by exact name. An unrecognised name is not an error — it simply never matches, so it contributes nothing.
- If you list only invalid names, you get an empty record object, not an error and not the full record. If every name you send is wrong, each returned record will be
{}. - Omit
Fieldsto get everything. An absent or emptyFieldsleaves the list empty andAllowableFieldreturnstruefor every field. - The filter applies to the output only. It does not reduce the work the server does, so it will not make a slow call fast.
- On POST endpoints, sending
Fieldshas a side effect: seeDetailedResponsebelow.
The separate, body-level Fields array (legacy endpoints only)
Older TServerMethodsWebAPI operations take a different mechanism — a JSON body element, sent with a leading capital at the top level of the request:
{ "Fields": [ "CustomerUnique", "CustomerName", "City" ] }
(The service rewraps it internally as a lowercase fields element before parsing; you always send the capitalised form.)
Handled by ServerMethodsBase.GetJSONFields:
- Names must match a field's public name exactly (case-sensitive). Public names on this surface differ from the newer classes —
CustomerUniqueandCustomerNamehere, versusUniqueandNameon theCustomerclass. - Unrecognised names are silently discarded.
- If no valid field survives, all valid fields are returned. This is the opposite of the query-string
Fieldsbehaviour above, which yields an empty object. Be careful not to confuse the two.
3. Filters — body-level filtering (legacy endpoints only)
Legacy *_Get operations accept a Filters array in the body, again with a leading capital at the top level:
{
"Filters": [
{ "Field": "CustomerName", "Operator": "=", "Value": "Smith" },
{ "Field": "City", "Operator": "like", "Value": "Vern" }
]
}
Do not add your own % wildcards to a like value — the server wraps the value in %...% itself. See The Legacy TServerMethodsWebAPI Surface for how the filter becomes SQL, and for the apostrophe caveat that follows from it.
The property names are Field, Operator, Value. (Some in-code comments show FieldName; the parser only recognises Field.)
Valid operators
=, <, >, <=, >=, <>, and like (case-insensitive). Anything else fails the request with:
Error in filter; invalid operator specified: {operator}
Which fields can be filtered
Only fields whose definition declares a filter type other than "none". Each domain publishes its own field list; a field you can read is not necessarily a field you can filter. An unfilterable or unknown field fails the request with:
Error in filter; invalid field specified: {field}
Silent-skip rule
A filter entry is only considered if all three of Field, Operator and Value are non-empty. An entry missing any one of them is skipped without comment. If every entry is skipped, the request fails with:
Filters specified; but no filters found
That message is therefore usually a symptom of a typo in a property name or an empty Value, not of a genuinely empty filter list.
Combination semantics
Filters are ANDed. There is no OR, no grouping and no negation beyond <>. Filtering a value of "" is not possible (it is treated as absent).
4. DetailedResponse — echoing the written record back
?DetailedResponse=Y
Applies to POST endpoints. Comparison is case-insensitive against Y; every other value, including true, 1 and Yes, is treated as no.
When on, each ActionResult entry in the response carries a populated RecordResult object containing the record as it now stands in System Five. When off, RecordResult is null.
Two things worth knowing:
FieldsimpliesDetailedResponse. InCM_Base.InitPostGetQueryParameters, ifFieldsis present and non-empty on a POST,fDetailedPostResponseis forced totrue. You can therefore turn on the detailed response simply by asking for fields, and you cannot ask for specific fields while suppressing the detail.- The detailed record is produced by re-reading the record after the write, so it reflects everything System Five derived (assigned numbers, computed totals, tax). This is the cheapest reliable way to learn an assigned invoice number or a computed total without a second round trip.
5. Pagination — PageSize and PageNumber
?PageSize=500&PageNumber=1
PageNumberis 1-based. Page 0 means "no pagination".PageSizeis capped at 50 000 (cMaxPageSizeinCM_Base). A larger value is silently reduced to the cap — you are not told. Some Swagger descriptions quote a lower figure (the Invoice endpoints say 2 500); treat those as advisory guidance about memory rather than an enforced limit, and the 50 000 cap as the hard one.PageSizewithoutPageNumberdisables pagination. InInitGetQueryParameters, if the endpoint supportsPageNumberand the parameter is absent, bothPageNumberandPageSizeare forced to0. You must send both or neither.PageNumberis only read at all whenPageSize > 0.- With pagination off, the endpoint returns everything that matches. On large datasets that means a very large JSON document built entirely in memory, while holding the service's global critical section. Several Swagger descriptions warn about memory errors here; the warning is real.
The count-then-page pattern
For the collections that offer a count operation, the intended sequence is:
- Call the
*RecordCount/*CountByDateoperation for the same criteria. - Compute
ceil(count / PageSize). - Loop
PageNumberfrom 1 to that many pages.
The count and the paged fetch are separate, unsynchronised calls. Records written between them shift the pages, so a busy dataset can produce duplicates or gaps at page boundaries. For "changes since" style syncs, prefer paging by an advancing EffectiveDateTime watermark over paging by number.
6. EffectiveDateTime — date and time handling
?EffectiveDateTime=2026-09-01T00:00:00
Parsed with Lib.TryIso8601ToDateTime.
- ISO 8601,
YYYY-MM-DDTHH:MM:SS, with no UTC offset and noZsuffix. The Swagger descriptions state this explicitly and the parser is strict. - The value is interpreted in the server's local time zone — the time zone of the machine running the service, not yours and not UTC. Convert before sending, and account for the server's DST transitions.
- A value that fails to parse produces an immediate failure envelope, for example:
{ "Response": "Failed",
"Reason": "Invalid Start Date found (01/09/2026); must be in ISO8601 format" }
- The comparison is inclusive: records dated on or after the value are returned.
- Date-only fields elsewhere in payloads are also parsed by the ISO 8601 helper (
TryGetJSONValueforTPervasiveDate), so use the same format for them. A date that fails to parse becomes the Pervasive zero date rather than an error.
7. Inventory-only query parameters
Honoured by the Inventory class GETs (CM_Inventory.InitGetQueryParameters):
| Parameter | Effect when set |
|---|---|
MarkedDeleted=Y | Include parts flagged deleted in System Five. Default is to exclude them. |
eCommerceExport=Y | Restrict results to parts flagged for e-commerce export. Default is no restriction. |
CurrencyCode={n} | Evaluate prices in this System Five currency. Also sets the underlying record's CurrentCurrency. |
Department={n} | Evaluate department-specific pricing against this department. Note this affects pricing only — it does not change which department your credentials are authorised for (see Authentication and Authorization). |
PriceLevel={n} | Return prices at this price level. |
MarkedDeleted and eCommerceExport use the same case-insensitive Y test as DetailedResponse; any other value means off.
8. FreeFormNameMap — free-form field headers
?FreeFormNameMap=Y
System Five free-form fields are stored by numeric id, with their human labels held separately as comment records. Records returned by the API carry the ids, not the labels.
Setting FreeFormNameMap=Y adds a FreeFormHeaders array to the response mapping each id to its configured header text, so you can label the values:
"FreeFormHeaders": [
{ "1": "Warranty Period", "FreeFormHeaderIsChanged": false },
{ "2": "Country of Origin", "FreeFormHeaderIsChanged": false }
]
Each entry is an object whose key is the free-form id as a string and whose value is the header text, plus a FreeFormHeaderIsChanged flag (always false on read; it exists for the write direction).
The map is loaded per domain from the comment file at FileNumber + 128 and is the same for every record in the response, so request it once per sync rather than on every page.
9. ConnectionInfo in POST bodies
Covered in Authentication and Authorization section 7. Summary: it is required in practice on the shared-pipeline POST endpoints, only TerminalNumber is read, and omitting it fails the entire request with Connection Info missing before any record is processed. Send:
{ "ConnectionInfo": { "TerminalNumber": 1 } }


