REST API
Solid Accounting can run a Local REST API Server on your computer. A script or tool on the same computer can use it to read your books and to create contacts, invoices and journal entries. Every write goes through the same checks as the app: entries must balance, closed periods stay closed, and every change is recorded in the Audit Trail.
The API is included with Professional and Accountant. It is not available on Standard.
Turn it on and get a key
- Open Settings → AI & Integrations. Under Apps & API, click Create a key….
- Type a name for the key (for example "Excel add-in"), choose what it can do, and click Create key. If the API server isn't running, creating a key starts it.
- Copy the key when it appears. It is shown once. Keys start with
tlk_, and only a hash of the key is stored, so a lost key can't be shown again; revoke it and create a new one.
You can also create a key with permissions and a rate limit of your choosing from Apps → Add app → Inbound API Key:
- Read — query accounts, contacts, balances, journal entries, items
- Write — create and update contacts, journal entries, invoices
- Import — batch import transactions through
/import - Rate limit — 1 to 600 requests per minute (default 60)
To start, stop or change the port, open Advanced under Apps & API in Settings → AI & Integrations (shown in Expert Mode). Revoke a key from the API keys list on the same card at any time. API docs there opens the explorer described below.
The server runs only while Solid Accounting is open with a company file. It listens on 127.0.0.1 (this computer only) on port 21384 unless you change it. In this release it can't be exposed to your network.
Make a request
All endpoints are under:
http://127.0.0.1:21384/api/v1
Send your key in the X-API-Key header. Authorization: Bearer tlk_… also works.
curl -H "X-API-Key: $SA_API_KEY" \
http://127.0.0.1:21384/api/v1/accountsA successful response wraps the result in data. List endpoints add meta with total, limit and offset:
{
"data": [ ... ],
"meta": { "total": 212, "limit": 50, "offset": 0 }
}An error returns an error object with a stable code you can switch on, a human-readable message, and sometimes details:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Entry does not balance: debits=10000 credits=9900",
"details": { "reason": "unbalanced", "debit_cents": 10000, "credit_cents": 9900, "difference_cents": 100 }
}
}Codes include UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, NO_FILE_OPEN and INTERNAL_ERROR. A write into a closed period returns VALIDATION_ERROR with details.reason = "closed_period". When you hit the rate limit you get HTTP 429 with a Retry-After header.
Conventions:
- IDs are UUID strings. Invoices refer to customers by
customer_id, not by name. - Money is whole cents as an integer:
12345means $123.45. - Dates are
YYYY-MM-DD. - Paging uses
?limit=and?offset=. - No duplicates on re-runs. Send
external_idandsource_idon an import or journal entry and a repeat of the same pair is skipped.
Endpoints
The full, current list with request and response shapes is served by your own copy of the app:
GET /api/v1/openapi.json— the OpenAPI specGET /api/v1/docs— an interactive explorerGET /api/v1/health— a liveness check
These three need no key. The main groups:
| Area | Endpoints |
|---|---|
| Company & accounts | GET /company, GET/POST /accounts, GET/PUT /accounts/{id}, GET /accounts/by-number/{num}, POST /opening-balances |
| Contacts & items | GET/POST /contacts, GET/PUT /contacts/{id}, GET /items, GET/POST /tax-codes, GET/POST /payment-terms |
| Journal entries | GET/POST /journal-entries, GET /journal-entries/{id}, POST /journal-entries/{id}/void, POST /import |
| Sales | GET/POST /invoices, GET /invoices/open, GET/POST /estimates, POST /estimates/{id}/convert, GET /credit-memos, POST /payments/apply |
| Purchases | GET /bills, GET /bills/open, GET/POST /purchase-orders, POST /purchase-orders/{id}/receive |
| Balances & reports | GET /balances, GET /trial-balance, GET /reports/income-statement, /reports/balance-sheet, /reports/cash-flow, /reports/ar-aging, /reports/ap-aging, /reports/1099 |
| Banking | GET/POST /reconciliations, GET /reconciliations/history, GET /bank-feeds/transactions, POST /import/csv-upload, POST /import/ofx-upload |
| Other lists | classes, locations, projects, budgets, fixed assets (/assets), loans, recurring transactions, drafts |
| Fiscal years | GET/POST /fiscal-years, GET /fiscal-years/{id}/periods, POST /fiscal-years/{id}/close |
| Review & audit | GET /review-queue, POST /review-queue/{id}/approve, GET /audit-log |
Bank feed endpoints also need active Updates & Cloud Services.
What the API checks for you
- Balance. An entry whose debits and credits differ is refused.
- Closed periods. Nothing posts into a closed period.
- Permissions. A key can only do what its Read / Write / Import permissions allow.
- Review first. A key set to Review first parks its writes in the Review Queue until someone approves them.
- Audit. Every write is recorded in the Audit Trail with the key's name.
A script can fail, but it can't unbalance your books.
Limits in this release
- This computer only. The API listens on
127.0.0.1. A cloud service such as Zapier or a webhook from Stripe can't reach it. Run your script on the computer where Solid Accounting is open. - No outbound webhooks. The app doesn't call your URL when something changes. A script on the same computer can poll, or listen to
GET /events(server-sent events). - Browser pages are blocked. The API sends no CORS headers, so a web page in your browser can't call it. Use a script or a native program.
Related
- Apps & integrations — Shopify, Stripe and the Inbound API Key
- AI assistants — connect an assistant that uses this API for you
- Audit Trail