Using the API
Pagination and filters
Page through any list with cursors, narrow it with field filters and text search, and sort it on documented fields.
Every list endpoint, such as List documents, List clients or List items, works the same way. You get one page at a time using an opaque cursor, and you narrow the list with plain query parameters named after fields.
Cursor pagination
Lists use cursor pagination (proposed).
| Query parameter | Default | Meaning |
|---|---|---|
limit | 50 | How many records per page, from 1 to 200 |
cursor | none | The next_cursor value from the previous page |
Every list response has the same envelope:
{
"data": [
{ "id": "0193f2b4-5a6b-7c8d-9e0f-1a2b3c4d5e6f", "number": "INV-2027-0042", "status": "sent" }
],
"next_cursor": "eyJ0IjoiMjAyNy0wMS0wNVQx...",
"has_more": true
}datais the records on this page.has_moreistruewhen another page exists.next_cursoris the value to pass ascursorto get that page, ornullon the last page.
The cursor is opaque: do not build, change or decode it. Keep every other query parameter the same when you pass it, so the next page continues the same filtered list.
Walk every page
# First page
curl "https://api.freeinvoiceautomation.com/v1/documents?type=invoice&status=overdue&limit=100" \
-H "Authorization: Bearer $FIA_API_KEY"
# Next page: same filters plus the cursor
curl "https://api.freeinvoiceautomation.com/v1/documents?type=invoice&status=overdue&limit=100&cursor=eyJ0IjoiMjAyNy0wMS0wNVQx..." \
-H "Authorization: Bearer $FIA_API_KEY"Order is stable
Unless you set sort, lists are ordered newest first by created_at, then by id, both descending. The order is stable, so the same query walks the records in the same order every time, and ties on created_at are broken by id.
Filters
Filters are plain query parameters named after the field they filter. The ones in common use are:
| Parameter | Example | Meaning |
|---|---|---|
business_id | business_id=0193f29e-... | Only records of this business |
client_id | client_id=0193f2a1-... | Only records of this client |
type | type=invoice | Document type: invoice, estimate, credit_note, receipt |
status | status=sent,overdue | One or more statuses |
currency | currency=EUR | Only records in this currency |
due_before, due_after | due_before=2027-02-01 | Due date range |
issued_after, issued_before | issued_after=2027-01-01 | Issue date range |
updated_after | updated_after=2027-01-10T00:00:00Z | Changed since a moment; useful for syncing |
external_ref | external_ref=5012 | Your own reference, such as a store order id |
archived | archived=true | Archived records instead of active ones |
Each endpoint page lists exactly which filters it accepts, so check the page before relying on a filter.
Several values
Pass several values separated by commas. They are combined with OR inside one parameter, and different parameters are combined with AND:
GET /v1/documents?type=invoice&status=sent,overdue¤cy=USDThis returns USD invoices that are sent or overdue.
Text search
q searches the main text fields of the resource: names, document numbers and email addresses.
GET /v1/clients?q=acme
GET /v1/documents?q=INV-2027-0042Sorting
Set sort to a documented field. A leading - sorts descending.
GET /v1/documents?sort=due_date
GET /v1/documents?sort=-total_minorSorting works on documented fields only; the endpoint page lists them, often as the allowed values of sort.
Reports take their own parameters
Report endpoints are not paginated lists. They take a period and filters, and a format:
| Parameter | Meaning |
|---|---|
from, to | The period, as dates |
client_id, currency, business_id | Narrow the report |
format | json, csv or pdf |
Keeping a copy in sync
To mirror records into your own system, combine updated_after with pagination: store the time of your last successful sync, ask for records updated after it, walk every page, then save the new time. For changes as they happen, subscribe to webhook events instead of polling, and use the list endpoints to catch up after downtime.
Limits and errors
limitmust be between 1 and 200. Query parameters are validated like any other input, and invalid values returnvalidation_failed(422) with the problem indetails.issues.- Pass the cursor back unchanged, with the same filters. If you change the filters, start again from the first page.
- Large lists count against rate limits one request per page, so use
limit=200for bulk work.