Quickstart: Query API tour
A guided, read-only tour of the Query API: filters,
field selection, nested loading, counting, metadata discovery, and one
aggregation. Every call below is read-only and ran live against
http://localhost:8080 — nothing in this quickstart writes or modifies
data. See the Query API reference for the complete
grammar; this page is the "try it in order" version.
Step count: 8 steps, all run live in this session.
Step 1: the simplest call — list with a field projection
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/api/v1/query/seller?limit=5&fields=code,description"
Response:
{"total":12,"limit":5,"offset":0,"data":[{"code":"SELLER_FR","description":"France Shops"},{"code":"SELLER_US","description":"US Seller"},{"code":"MAIN_SELLER","description":"Demo Distributor"},{"code":"INWI_MA","description":"Inwi Morocco"},{"code":"NETFLIX_MA","description":"Netflix Maroc"}]}
total is the full count of sellers on this stack (12 at the time this was
captured), independent of limit.
Step 2: filter by a condition keyword
ne (not equals):
curl -G -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'filters={"ne code":"SELLER_FR"}' \
--data-urlencode 'fields=code,description' \
--data-urlencode 'limit=5' \
http://localhost:8080/api/v1/query/seller
Response:
{"total":11,"limit":5,"offset":0,"data":[{"code":"SELLER_US","description":"US Seller"},{"code":"MAIN_SELLER","description":"Demo Distributor"},{"code":"INWI_MA","description":"Inwi Morocco"},{"code":"NETFLIX_MA","description":"Netflix Maroc"},{"code":"OPENAI_MA","description":"OpenAI Morocco"}]}
wildcardOr (substring match, auto-wraps the value in *...*):
curl -G -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'filters={"wildcardOr description":"Morocco"}' \
--data-urlencode 'fields=code,description' \
http://localhost:8080/api/v1/query/seller
Response:
{"total":2,"limit":100,"offset":0,"data":[{"code":"INWI_MA","description":"Inwi Morocco"},{"code":"OPENAI_MA","description":"OpenAI Morocco"}]}
The full condition-keyword table has every verified operator: ranges, list membership, nested boolean groups, raw SQL fragments, and more.
Step 3: nested-entity loading
nested eagerly loads a related entity so you get its fields inline instead
of just an id reference:
curl -G -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'filters={"code":"CUST_SARA"}' \
--data-urlencode 'fields=code,description,customerAccount.code,customerAccount.customer.code' \
--data-urlencode 'nested=customerAccount' \
http://localhost:8080/api/v1/query/billingAccount
Response:
{"total":1,"limit":100,"offset":0,"data":[{"code":"CUST_SARA","description":"Sara Benali","customerAccount":{"id":78,"code":"CUST_SARA","description":"Sara Benali","customer":{"id":77}}}]}
Without nested=customerAccount, customerAccount would come back as a
bare {"id":78} reference. depth controls how many levels of nesting
follow automatically past what nested names explicitly.
Step 4: count without fetching rows
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/query/seller/count
Response:
{ "total": 12 }
Cheaper than a list call when you only need the number, and usable with the
same filters parameter as the list endpoint.
Step 5: an aggregation — groupBy + SUM()/COUNT()
Putting a parenthesized aggregation expression in fields switches to the
native aggregation path (see groupBy / having +
aggregation for exactly which
functions are recognized and why the space-separated form is a different,
unrelated mechanism):
curl -G -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'fields=seller.code,SUM(amountWithoutTax),COUNT(id)' \
--data-urlencode 'groupBy=seller.code' \
--data-urlencode 'limit=5' \
http://localhost:8080/api/v1/query/walletOperation
Response:
{"total":2,"limit":5,"offset":0,"data":[{"seller.code":"PLUXEE_BE","SUM(amountWithoutTax)":-100.600000000000,"COUNT(id)":4746},{"seller.code":"EDENRED_FR","SUM(amountWithoutTax)":-48.000000000000,"COUNT(id)":1}]}
One row per group; total here means "2 groups," not "2 wallet
operations."
Step 6: get a single record by id
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/api/v1/query/seller/-3?fields=id,code,description"
Response:
{"data":{"id":-3,"code":"SELLER_FR","description":"France Shops"}}
Note the different envelope shape from list/count: a single data object,
no total/limit/offset.
Step 7: discover what you can query (metadata endpoints)
List every queryable entity, or narrow to just the ones flagged
@HugeEntity in the domain model:
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/api/v1/query/entities?onlyHugeEntities=true"
Response (truncated to the first 10 of 48):
{"entities":["Access","AccountEntity","AccountOperation","Attribute","AuditLog","AutomatedPayment","AutomatedRefund","BillingAccount","CDR","ChargeTemplate", "...38 more"]}
Get the field-level schema (column names, FK/association flags) for one entity:
curl -H "Authorization: Bearer $TOKEN" "http://localhost:8080/api/v1/query/entities/seller"
Response (excerpt):
{
"javaName": "Seller",
"tableName": "crm_seller",
"fields": [
{"javaField": "billingTemplateName", "javaType": "String", "columnName": "billing_template_name"},
{"javaField": "generalLedger", "javaType": "GeneralLedger", "isFK": true, "columnName": "general_ledger_id", "isAssociation": true},
{"javaField": "legalText", "javaType": "String", "columnName": "legal_text"},
{"javaField": "legalType", "javaType": "String", "columnName": "legal_type"},
{"javaField": "seller", "javaType": "Seller", "isFK": true, "...": "..."}
]
}
Useful for building a query UI or a codegen step against entities you don't already know the shape of.
Step 8: same query, both dialects, byte-identical result
The GET dialect (used throughout this page) and the POST dialect (JSON
body, same shape the legacy Generic API accepts) are the same
implementation underneath. Same filter as step 2's wildcardOr example,
sent as a POST body instead:
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"filters":{"wildcardOr description":"Morocco"},"genericFields":["code","description"]}' \
http://localhost:8080/api/v1/query/seller
Response (identical to step 2's GET call):
{"total":2,"limit":100,"offset":0,"data":[{"code":"INWI_MA","description":"Inwi Morocco"},{"code":"OPENAI_MA","description":"OpenAI Morocco"}]}
Note the POST dialect keeps the legacy field name genericFields, not
fields — see migrating from the Generic
API for the complete field
mapping between the two dialects.
Where to go next
- The full filter operator table —
every verified condition keyword, including the ones not demonstrated
here (
inList,minmaxRange,anyMatch,$filterNnesting,cfValues). - The
$operatorand$filterNnested filter grammar — how to build nested OR filters (both keys accept any case, e.g.$operatoror$OPERATOR). - The catalog-to-first-invoice and usage mediation quickstarts for how the entities queried here (sellers, charges, wallet operations, rated transactions) get created in the first place.