Purchase line items
MendriX calculates, per ride, what that ride is expected to cost when an operator (a charter, a subcontractor) drives it. Those calculated costs are the ride’s purchase line items. This guide reads them out of the Purchase service, so the invoices you receive from an operator can be reconciled against what MendriX expected to pay.
One call answers with the line items of one operator, narrowed to a ride or a period, plus the ride, task and operator data those lines refer to.
Before you start
Section titled “Before you start”- An API token exchanged for an access token — see Authentication.
- The token’s user needs the Purchase invoicing permission. Without it the
call answers
403. - The operator you are reconciling needs an operator number in MendriX (at most 12 characters). It defaults to the operator’s internal id but can be changed, and it is the value you identify the operator with. MendriX does not enforce that it is unique, so if two operators share one, the API says so rather than guessing (see Errors).
Step 1 — Identify the operator
Section titled “Step 1 — Identify the operator”Every list call identifies exactly one operator, with either
operatorNumber or operatorId — never both, never neither. Supplying both
is rejected even when they point at the same operator.
| Parameter | Use it when |
|---|---|
operatorNumber | You know the operator by the number your MendriX administration uses (the usual case). |
operatorId | You already store the internal id, as the operatorId fields in earlier responses carry it. |
Step 2 — Narrow to a ride or a period
Section titled “Step 2 — Narrow to a ride or a period”Next to the operator, a narrowing criterion is required: rideId,
rideReference, or fromDate together with toDate. Without one the request is
rejected with 400 — otherwise a single call could pull an operator’s entire
purchase history. Supplying several narrows further; they combine with AND.
# All purchase line items of one ride.curl "$MENDRIX_BASE/purchase/line-items/?operatorNumber=88&rideId=123" \ -H "Authorization: Bearer $MENDRIX_ACCESS_TOKEN" \ -H "Accept: application/json"
# Everything the operator drove in June 2026.curl "$MENDRIX_BASE/purchase/line-items/?operatorNumber=88&fromDate=2026-06-01&toDate=2026-07-01" \ -H "Authorization: Bearer $MENDRIX_ACCESS_TOKEN"
# By the reference entered on the ride (ritkenmerk). It is not guaranteed to be unique, so this# may return the lines of several rides.curl "$MENDRIX_BASE/purchase/line-items/?operatorNumber=88&rideReference=PO-88123" \ -H "Authorization: Bearer $MENDRIX_ACCESS_TOKEN"The period is half-open: fromDate is included, toDate is not. Pass the
start of the next period as toDate — June is 2026-06-01 to 2026-07-01, not
to 2026-06-30. Consecutive periods then neither overlap nor leave a gap.
The date matched is the planned start date of the ride the line was calculated for.
- Schedule Trigger — e.g. hourly, or daily if you reconcile per period.
- HTTP Request
Get access token— the login node from Using n8n (Custom Auth =MendriX API token). - HTTP Request
List purchase line items(GET /purchase/line-items/):Authorizationheader =Bearer {{ $('Get access token').first().json.data.items[0].access }}, queryoperatorNumber,fromDate,toDate,limit=100. - Split Out on
data.itemsto get one item per line. - Look the ride, tasks and operator up in
data.context(see step 3) and write the result into your own reconciliation table.
Importable workflow: purchase-line-items.json.
Step 3 — Read the response
Section titled “Step 3 — Read the response”The lines are in data.items. The ride, task and operator data they refer to is
not repeated on every line: it is returned once per response in
data.context, as three lists to look up by the rideId, taskIds and
operatorId on a line.
{ "data": { "code": 200, "items": [ { "id": "556677", "rideId": 123, "operatorId": 88, "taskIds": [201, 202], "code": "TRANSPORT", "description": "Purchase transport", "revenueGroup": "TRANS", "quantity": 2, "freeQuantity": 0.5, "unitPrice": 850, "additionalAmount": 15, "minimumAmount": 100, "amount": 1290, "currencyCode": "EUR", "date": "2026-06-10T00:00:00+02:00", "status": "open" } ], "itemCount": 1, "maxSequence": "40155", "nextCursor": "", "hasMore": false, "context": { "rides": [ { "rideId": 123, "date": "2026-06-10T08:00:00+02:00", "reference": "PO-88123", "nameManual": "Fixed Tuesday ride", "nameAutomatic": "Amsterdam - Rotterdam" } ], "tasks": [ { "taskId": 201, "type": "load", "dateTime": "2026-06-10T08:00:00+02:00", "yourReference": "ORD-4711", "ourReference": "MX-2026-0456", "location": { "name": "DC Amsterdam", "city": "Amsterdam", "country": "NL" } }, { "taskId": 202, "type": "unload", "dateTime": "2026-06-10T12:00:00+02:00", "yourReference": "ORD-4711", "ourReference": "MX-2026-0456", "location": { "name": "Client Rotterdam", "city": "Rotterdam", "country": "NL" } } ], "operators": [ { "operatorId": 88, "operatorNumber": "88", "vatNumber": "NL123456789B01", "name": "Jansen Transport BV" } ] } }}Fields on a line
Section titled “Fields on a line”| Field | Meaning |
|---|---|
id | Id of the line item. Stable for as long as the line exists — see step 5. |
rideId | Ride this line belongs to; its details are in context.rides. |
operatorId | Operator the purchase is owed to; details in context.operators. |
taskIds | Tasks this line covers; details in context.tasks. A leg line references its load and unload task, a ride-level line every task of the ride. |
code | Code of the purchase article; empty for a manual line. |
description | Line description as the planner sees it. |
revenueGroup | Revenue group code of the article; empty for a manual line. |
quantity, freeQuantity | The quantity the unit price is charged over, and the part of it that is free of charge. |
unitPrice, additionalAmount, minimumAmount | Price per unit, the surcharge on top of quantity times unit price, and the amount the line is raised to when it would otherwise fall below it. |
amount | The expected amount, and the figure to reconcile against. It is net — the purchase calculation knows no VAT. In the example: (2 - 0.5) × 850 + 15 = 1290. |
currencyCode | ISO currency code of every amount on the line; always EUR. |
date | Planned start date of the ride the purchase was calculated for, in local time. |
status | open while the line is not matched to a purchase invoice line, matched once it is. |
operatorId and date are null on a line whose purchase has not been
calculated for a planned ride yet. Such a line cannot be found by operator, by
period or with sort=date, so it never turns up in a list result — only in a
lookup by id.
Context
Section titled “Context”rides—date(planned start),reference(the ritkenmerk, empty when none was entered and not guaranteed unique),nameManual(the name the planner typed, empty when the name was left automatic) andnameAutomatic(the name MendriX derived from the route).tasks—type(load,unload, orunknownfor any other kind of task),dateTime(planned moment, or the realised one when no planned moment is known),yourReference(uw kenmerk),ourReference(ons kenmerk) andlocation(name,city, two-letter ISOcountry).operators—operatorNumber(the value to identify the operator with), plusvatNumberandnameas verification fields: check them against the invoice you received, but never use them as a search key.
A ride whose lines span two pages appears in the context of both pages.
Step 4 — Page through the result
Section titled “Step 4 — Page through the result”This endpoint pages like the rest of the API (see
Pagination & sync), but with lower
page sizes: limit defaults to 10 and is capped at 100. A larger
limit is accepted but clamped.
The reason is the context block: one line can pull in its whole ride and every
task of that ride, and a ride can hold dozens of tasks, so the response grows
with that context rather than with the number of lines. To cover more ground,
narrow the query by ride or period and follow nextCursor.
sort supports id (the default) and date, each with a - prefix for
descending. Keep the same sort for every page of one walk: a cursor resumed
under a different ordering is rejected with 400.
Step 5 — Keep in sync
Section titled “Step 5 — Keep in sync”For a recurring job, ask only for what changed: pass the maxSequence of your
previous run back as since=.
curl "$MENDRIX_BASE/purchase/line-items/?operatorNumber=88&fromDate=2026-06-01&toDate=2026-07-01&since=$WATERMARK" \ -H "Authorization: Bearer $MENDRIX_ACCESS_TOKEN"since=is a sequence number, not a date — themaxSequencefrom the response envelope. Store it after a successful cycle and pass it back next time. It is exclusive: you get the lines whose sequence is greater than the value you pass.- Bootstrap with
since=1. Only a request carryingsince=reports amaxSequencethat is a real high-water mark, frozen for the whole cycle; without it the field is just the highest sequence on the page you fetched. So start withsince=1— it reads everything in the period and gives you a watermark to keep. since=requires the default sort; combining it withsort=is rejected with400.- Recalculating a ride’s purchase re-stamps all of that ride’s lines, so a sync run re-delivers lines that did not themselves change. Make your upsert idempotent.
- Deletions are not visible in this feed. A removed line simply stops being
returned; nothing marks it as gone. Run a periodic full resync — re-read the
period with
since=1— and treat whatever it does not return as gone. - An
idstays the same for as long as its line exists, which is what makes it usable to refer back to, but it is not a permanent reference. Treat the list as the source of truth and expect a stored id to stop resolving at some point.
One line item by id
Section titled “One line item by id”curl "$MENDRIX_BASE/purchase/line-items/556677" \ -H "Authorization: Bearer $MENDRIX_ACCESS_TOKEN"Answers the same envelope as the list — data.items holds that single line and
data.context its ride, tasks and operator — and takes no parameters at all,
not even an operator key: the id identifies the line on its own.
It answers 404 once the line is no longer part of the operator’s current
purchase, for instance because it was removed or because the ride was replanned.
Whatever the list no longer returns, this endpoint no longer returns either.
Parameters
Section titled “Parameters”GET /purchase/line-items/ — the full contract is in the
reference.
| Parameter | Type | Notes |
|---|---|---|
operatorNumber | string | Operator number as set up in MendriX, at most 12 characters. Exactly one of this and operatorId. |
operatorId | integer | Internal id of the operator. Exactly one of this and operatorNumber. |
rideId | integer | Only line items of this ride. |
rideReference | string | Only line items of rides carrying this reference. Not unique; must not be empty. |
fromDate | date | Start of the period, inclusive. Requires toDate. |
toDate | date | End of the period, exclusive. Requires fromDate. |
status | open | matched | Defaults to both. |
since | integer | Only lines changed after this sequence number. |
sort | id | date | - prefix for descending. Default id. |
limit | integer | Default 10, maximum 100. |
cursor | string | The nextCursor of the previous page. |
At least one of rideId, rideReference, or fromDate + toDate is required.
GET /purchase/line-items/{lineItemId} takes no parameters.
Errors
Section titled “Errors”Next to the common status codes:
| Status | Cause |
|---|---|
400 | Both operatorNumber and operatorId, or neither. No narrowing criterion. An empty rideReference. Only one of fromDate / toDate, a non-ISO date, or toDate not later than fromDate. An unknown status. A non-numeric since. since combined with sort, or a cursor resumed under a different sort. |
403 | The token’s user lacks the Purchase invoicing permission. |
404 | No operator with that number or id (a removed operator answers the same as an unknown one). Or, on the by-id endpoint, no such line item. |
409 | The operator number is in use by more than one operator, so it does not identify one. Make the number unique in MendriX and retry. |
Reconciliation tips
Section titled “Reconciliation tips”- Reconcile net.
amountexcludes VAT; an operator’s invoice total usually includes it. - Verify the party. Check
context.operators[].vatNumberandnameagainst the invoice before booking anything against the lines. - One invoice line rarely equals one purchase line item. An operator’s invoice line can cover one article of one ride, a whole ride, or one article across many rides — a diesel surcharge over a period, say. Match one invoice line to the set of purchase line items it covers.
- Watermark after success, so a failed run safely re-reads next time.