RSQL
Public API — RSQL Filtering
Every major list resource has a companion rsql-list endpoint that accepts one expressive filter string instead of dozens of query parameters — with and/or, parentheses, nested relation filters, sorting, and (for infrastructure resources) asynchronous CSV export.
Endpoints
All are POST, paginated with the standard ?page=&limit= envelope (see General guide):
| Resource | Endpoint | Permission | CSV export |
|---|---|---|---|
| Sessions | /api/v1/sessions/rsql-list | session:read | — |
| CDRs | /api/v1/cdrs/rsql-list | cdr:read | — |
| Charging pools | /api/v1/charging-pools/rsql-list | charging-pool:read | yes |
| Charging stations | /api/v1/charging-stations/rsql-list | charging-station:read | yes |
| Charging points | /api/v1/charging-points/rsql-list | charging-point:read | yes |
| Connectors | /api/v1/connectors/rsql-list | connector:read | yes |
| Station OCPP logs | /api/v1/charging-stations/{id}/ocpp-logs/rsql-list | charging-station:read-logs | — |
Request body — both fields optional:
curl -s -X POST -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
-d '{
"filter": "status == '\''COMPLETED'\'' and startDate =gte= '\''2026-01-01T00:00:00Z'\''",
"sort": "startDate desc"
}' \
"https://<host>/api/v1/sessions/rsql-list?page=1&limit=50"filter— RSQL expression (syntax below). Omitted → no filtering.sort— comma-separatedfield directionpairs, directionascordesc(e.g."name asc, createdAt desc"). Any scalar filterable field is sortable. Omitted → endpoint default order.
Filter syntax
-- comparison
status == 'ACTIVE'
name != 'Test'
totalEnergy =gt= 10
totalEnergy > 10 -- shorthand: > >= < <= map to =gt= =gte= =lt= =lte=
startDate =gte= '2026-01-01T00:00:00Z'
-- logic: keyword operators, case-insensitive; and binds tighter than or
status == 'ACTIVE' and currency == 'EUR'
(status == 'ACTIVE' or status == 'COMPLETED') and currency == 'EUR'
-- value lists
status =in= ('ACTIVE', 'COMPLETED')
status =out= ('RESERVATION')
-- text (strings only)
name =contains= 'Park'
name =starts= 'EV'
-- null checks
stopDate =is= null
stopDate =isnot= null
-- ranges (value is a 2-element list)
totalEnergy =between= (5, 50)
-- nested to-one relation: dot notation
connector.powerType == 'DC'
-- nested to-many relation: dot notation = "at least one related record matches"
chargingPoints.status == 'AVAILABLE'
-- scoped sub-filter: all conditions must hold on the SAME related record
chargingPoints =any= (status == 'AVAILABLE' and connectors =any= (standard == 'CHADEMO'))
-- =all= : every related record matches
connectors =all= (status == 'AVAILABLE')Operators
| Operators | Meaning | Field types |
|---|---|---|
== != | equal / not equal | all |
=gt= =gte= =lt= =lte= (or > >= < <=) | comparison | number, date |
=between= =notbetween= | inclusive range, value (low, high) | number, date |
=in= =out= | membership in value list | string, enum, ID, number |
=contains= =starts= =ends= (+ =notcontains= =notstarts= =notends=) | text match | string |
=is= =isnot= | null check, value must be null | nullable fields |
=anyin= =allin= | array field contains some / all of the values | scalar arrays (e.g. tags) |
=any= =all= | scoped sub-filter on a relation | list relations |
=anyeq= =anygt= =anylte= … =alleq= =allgt= … =allof= =anyof= | quantified comparison over a related collection | relation paths |
Values
| Type | Format |
|---|---|
| String / enum | 'single' or "double" quotes; case-sensitive |
| Number | bare literal, negative allowed: =gt= -5 |
| Boolean | true / false (bare) |
| Date | ISO 8601, quoted — unquoted : breaks the parser |
| Null | bare null with =is= / =isnot= |
| Value list | parenthesized, comma-separated: ('A', 'B') |
Discovering filterable fields
Each endpoint publishes its own filter spec — fields, types, allowed operators, enum values:
curl -s -H "x-api-key: $API_KEY" \
"https://<host>/api/v1/sessions/rsql-list/configuration"Some string/ID fields additionally offer GET .../configuration/{fieldPath}/autocomplete?query=... (typeahead values) and GET .../configuration/{fieldPath}/dictionary (ID → label pairs) — the configuration response marks which. The Swagger UI also embeds every endpoint's field list in its operation description.
CSV export
The four infrastructure endpoints export matching rows asynchronously:
# 1. Schedule — same filter/sort body, returns 201 with an export id
curl -s -X POST -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
-d '{ "filter": "status == '\''ONLINE'\''", "sort": "createdAt desc" }' \
"https://<host>/api/v1/charging-stations/rsql-list/export-csv"
# 2. Poll until finished
curl -s -H "x-api-key: $API_KEY" "https://<host>/api/v1/rsql-csv-exports/<id>"Status response: { id, finished, failed, errorMessage, downloadUrl, createdAt } — downloadUrl is a signed URL, null until finished is true. Export status is only visible to the API key that scheduled it.
Errors
Invalid filters return 400 with a precise message:
{ "statusCode": 400, "error": "Bad Request", "message": "Unknown field: xyz" }
{ "statusCode": 400, "error": "Bad Request", "message": "Operator '=contains=' is not allowed for 'status'. Allowed: ==, !=, =in=, =out=" }Gotchas
;and,are NOT logical operators (unlike standard RSQL) — use theand/orkeywords. A bare,only separates values inside(...)lists.- Quote datetime values —
startDate =gte= "2026-01-01T00:00:00Z"; the unquoted:is a parse error. - String matching is case-sensitive:
status == 'active'does not matchACTIVE. - Dot notation on a to-many relation is an implicit
=any=— and two dot-notation conditions on the same relation may be satisfied by different related records. Userelation =any= (cond1 and cond2)to force same-record matching. - Numbers against string fields are coerced (
address =contains= 27works); booleans and dates in quotes are coerced too.
Related
- General guide — pagination envelope, error shapes
- Session Flows, Tariffs & Pricing — resources you'll filter most
Updated 7 days ago