Using the API
Requests and responses
How to shape requests to the API, which headers to send, and what successful responses look like for single resources, lists, creates, deletes and jobs.
Every endpoint follows the same rules for URLs, bodies, headers and responses. Learn them once and every page in this reference reads the same way. This page covers the request side, the success envelopes and the headers you will see. Errors have their own page, Errors.
URLs and methods
All paths start at the base URL https://api.freeinvoiceautomation.com/v1.
- Resources are plural nouns:
/documents,/clients,/items,/payments. - Actions that are not a plain create or update are sub-resources named after the action:
POST /documents/{id}/send,POST /documents/{id}/void,POST /documents/{id}/credit-notes. - Business-owned resources use flat paths with a
business_idfield (proposed): in the body for writes and in the query for reads. If you can reach exactly one business, you may leavebusiness_idout.
| Method | Use |
|---|---|
GET | Read a resource or a list. Never changes anything. |
POST | Create a resource, or run an action such as send or void. POST /clients and POST /items with an existing id update that record (upsert). |
PATCH | Partial update. Send only the fields you want to change (JSON merge). |
PUT | Replace a whole sub-resource, such as a business's accepted payment methods. |
DELETE | Soft delete. Returns 204 with no body. |
Request bodies
- JSON only, UTF-8. Send
Content-Type: application/json. A body that is not valid JSON returnsinvalid_json(400). - Field names are
snake_case. - Ids are uuid v7 strings.
- Dates are
YYYY-MM-DD; timestamps are RFC 3339 in UTC, such as2027-01-04T10:15:00Z. - Money is a string of integer minor units plus a
currencyfield."unit_price_minor": "45000"with"currency": "USD"is 450.00 USD. Numbers with decimals are rejected. See Money and currencies. - File uploads (such as a CSV import or a logo) use
multipart/form-data, as shown on the endpoint page.
Every input is validated before anything is saved. When a field is wrong you get validation_failed (422) with the list of problems in details.issues:
{
"error": {
"code": "validation_failed",
"message": "Some fields are not valid.",
"details": {
"issues": [
{ "path": ["lines", 0, "quantity"], "message": "Must be greater than 0" }
]
},
"request_id": "req_0193f2a2c0e0"
}
}A partial update
PATCH changes only the fields you send. Fields you leave out keep their values.
curl -X PATCH "https://api.freeinvoiceautomation.com/v1/documents/0193f2b4-5a6b-7c8d-9e0f-1a2b3c4d5e6f" \
-H "Authorization: Bearer $FIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9a41c0de-update-due" \
-d '{ "due_date": "2027-02-15" }'A sent document is never edited in place. Updating it creates a new revision, so what your client received stays on record. See Revisions.
Request headers
| Header | When | Notes |
|---|---|---|
Authorization | Every authenticated call | Bearer <api key or MCP token>. See Authentication. |
Content-Type | Calls with a body | application/json, or multipart/form-data for uploads |
Idempotency-Key | Every write; required on creates, sends, payments, voids and credit notes from API keys and MCP clients | Up to 255 characters. See Idempotency. |
FIA-Account-Id | Web app sessions only (proposed) | Chooses the account when a person belongs to several. API keys never need it. |
Success responses
A single resource
A single resource is wrapped in data:
{
"data": {
"id": "0193f2a1-7c4e-7b9a-9f10-3c2d1e0a4b51",
"business_id": "0193f29e-11aa-7c3b-8d2e-5a6b7c8d9e0f",
"name": "Logo design",
"unit_price_minor": "45000",
"currency": "USD",
"created_at": "2027-01-04T09:12:44Z",
"updated_at": "2027-01-04T09:12:44Z"
}
}A list
Lists return the page in data, plus next_cursor and has_more (proposed envelope). See Pagination and filters.
{
"data": [ { "id": "0193f2a1-7c4e-7b9a-9f10-3c2d1e0a4b51", "name": "Logo design" } ],
"next_cursor": null,
"has_more": false
}Status codes for success
| Status | When |
|---|---|
200 OK | A read, an update, or an action that finished |
201 Created | A new resource; the Location header holds its URL |
202 Accepted | Work that runs in the background: account exports, accounting backfills, bank statement imports, agent runs. The body carries the id of a job or resource that you poll. |
204 No Content | A delete. There is no body. |
Documents carry their totals
Documents always come back with the totals the server computed: subtotal_minor, discount_minor, tax_minor, total_minor, amount_paid_minor, amount_credited_minor and balance_minor, plus status and number. They also include links to related documents (credit notes, the estimate an invoice came from, a contract), the payment_schedule when there is one, and pay_url when a share link exists. Do not calculate totals yourself; read them from the response.
Response headers
| Header | Meaning |
|---|---|
X-Request-Id | The id of this request (proposed name). The same value appears as request_id in errors. Quote it when you contact support. |
Idempotent-Replayed: true | The response is a stored copy of an earlier request with the same Idempotency-Key. |
RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset | Your rate-limit budget (proposed names). See Rate limits. |
Retry-After | Sent with 429 rate_limited: how many seconds to wait. |
Location | Sent with 201: the URL of the new resource. |
Deprecation | Sent when an endpoint or field is scheduled for removal in a later major version (proposed). |
Async jobs
Some work takes longer than a request should. These endpoints answer 202 Accepted at once with an id, and you fetch the result later:
curl -X POST "https://api.freeinvoiceautomation.com/v1/account/exports" \
-H "Authorization: Bearer $FIA_API_KEY" \
-H "Idempotency-Key: 4d2e8b10-export-jan"Then poll GET /v1/account/exports/{id} until it is ready. For agent runs, poll Get an agent run, or subscribe to the agent_run.completed and agent_run.failed webhook events instead of polling.
What the API never returns
- Secrets. Connection secrets, SMTP passwords, AI keys and webhook signing secrets are write-only. You can set or replace them, but no response contains them; at most you see the last four characters.
- Other accounts' data. Records you cannot reach answer
404 not_found, the same as records that do not exist. - Card numbers. Card data never reaches our servers. Payments are taken on hosted checkout pages.