Pulling documents
Orders, invoices, payments and returns are all pulled the same way: by version, never by
page number. Every change to a record gives it a higher version, and versions are handed out
in commit order, so a cursor can’t skip a record that was being written while you read.
GET /orders?filter[version_after]=0&per_page=100→ {"data": [ …oldest change first… ], "meta": {"per_page": 100, "has_more": true, "next_version_after": 18342, "high_water": 18990}}
GET /orders?filter[version_after]=18342&per_page=100→ … until "has_more": falseThe loop, on a schedule
Section titled “The loop, on a schedule”- Read your watermark: the highest
versionyou have staged, or0the first time. GET /{orders|invoices|payments|returns}?filter[version_after]=<watermark>&per_page=100.- Stage every row under a unique key
(id, version)— andline_numberfor line tables. A record that changes while you read comes back later with a higher version; the unique key makes the second copy harmless. - Repeat with
meta.next_version_afteruntilmeta.has_moreisfalse. - Book from staging, then ack what you booked.
Filters
Section titled “Filters”Every collection takes these:
| Parameter | Meaning |
|---|---|
filter[version_after] |
The cursor. |
filter[updated_after]=2026-10-01T00:00:00Z |
First load only: start at the first change from that time. Not with a cursor. |
filter[ack]=pending |
Only records not acked at their current version. Also accepted, rejected, any (default). |
filter[id]=a,b,c |
Only these ids — at most 100 (more answers 400 INVALID_REQUEST). |
filter[external_ref]=SO-0012345 |
Only the record you acked under that key. |
per_page |
1–500, default 100. page= and offset= are refused. |
filter[status]=ERP_PENDING,ERP_FAILED |
Orders only. |
filter[seq_after] |
Invoices, payments and returns only — see the invoice counter. |
Brackets may be sent raw (filter[ack]) or encoded (filter%5Back%5D). An unknown filter[…]
is refused, not ignored, so a typo can’t silently widen a query.
Recovering after a lost cursor
Section titled “Recovering after a lost cursor”If your staging table is purged or the ERP database restored, the watermark is gone. Don’t start
from 0 and re-book: pull filter[ack]=pending instead. It returns exactly the records you
haven’t acked at their current version — including ones that changed after you acked them.
What each document carries
Section titled “What each document carries”- Order: header; customer, branch and rep with both keys; lines (product, unit, quantity,
price, discount, free-goods flag, promotion) with VAT per line worked out as the invoice will;
totals;
status;idempotency_key; and your lastack. - Invoice: as issued — lines, VAT, totals — its order with that order’s lines, and its state
with the tax authority (
tax_submission). - Payment: method (
CASH,CHEQUE,TRANSFER,INSTAPAY,WALLET), amount, allocations to invoices, the receipt, the cheque and its state, and areversalwhen undone. - Return: lines against the original invoice’s lines, the reason, the approval, and the credit note number.
Try it
Section titled “Try it”The loop in curl and jq, printing each order’s id, version and number. -g stops curl
reading the brackets as a pattern:
v=0while :; do page=$(curl -sg "$SLFLO_BASE/orders?filter[version_after]=$v&per_page=100" -H "Authorization: Bearer $SLFLO_TOKEN") echo "$page" | jq -r '.data[] | "\(.id) \(.version) \(.number)"' [ "$(echo "$page" | jq .meta.has_more)" = true ] || break v=$(echo "$page" | jq .meta.next_version_after)doneecho "watermark: $v"The same in Python, with staging and booking, is the pull connector.

