API reference
Public v1 endpoints, headers, response envelopes, and error codes.
The API base URL is https://api.accountingkit.dev. Every public endpoint is under /v1 and
requires Authorization: Bearer <api-key>. The key selects the environment, so an acct_test_ key
can never read or write live data.
curl "https://api.accountingkit.dev/v1/connections" \
--header "Authorization: Bearer $ACCOUNTINGKIT_API_KEY"Response envelopes
Every JSON success response uses the same shape, and list endpoints add pagination alongside it.
{"success": true, "message": "Invoices retrieved", "data": []}Branch on code, never on message
code is a stable contract. Messages are written for humans and change without notice.
| Status | Code | When it happens |
|---|---|---|
400 | BAD_REQUEST | Contract validation failed, or the request is malformed. |
401 | UNAUTHORIZED | The API key is missing, invalid, or revoked. |
403 | FORBIDDEN | The key is valid, but your plan does not allow this action or another connection. |
404 | NOT_FOUND | The connection, resource, or operation does not exist for this key. |
409 | CONFLICT | Idempotency conflict, a disallowed provider transition, or a revoked connection. |
413 | PAYLOAD_TOO_LARGE | The request body exceeds 10 MB. |
429 | RATE_LIMITED | The connection's provider request budget is temporarily exhausted. |
500 | INTERNAL | An unexpected failure. Safe to retry with the same idempotency key. |
503 | UNAVAILABLE | The provider or a dependency is unavailable. Retry with backoff. |
A 429 carries retry_after_seconds. The budget is per connection, so a burst on one tenant cannot
consume another tenant's provider allowance.
A 409 on a write almost always means an Idempotency-Key was reused with a different body.
Generate one key per logical operation and reuse it only for retries of that same operation. A
concurrent retry that arrives while the first is still running returns
The idempotent request is still in progress rather than duplicating the provider write.
Every request and response carries X-Request-ID. Send your own to correlate across systems, or use
the one returned. Include it in any support request — it is how a specific call is found.
Financial reports
Fetch a live Xero Profit and Loss report in the organization's base currency
Supply inclusive from_date and to_date (YYYY-MM-DD). basis defaults to accrual; use cash
for cash-basis reporting. The response contains the dates, currency, basis, fetch timestamp, and
hierarchical report.rows with string-valued cells and account attributes. No report body is
cached. Each request makes two budgeted provider reads (organization currency and report), billed as
one API operation plus actual provider egress.
Reporting consent is opt-in: include "reports": ["profit_and_loss"] when creating a connect
session. Reauthorise an existing connection with the same external account, environment and provider
app. Missing reporting consent returns 409 with details.reason: "missing_scope"; ordinary
invoicing remains available. The granular scope is accounting.reports.profitandloss.read; legacy
accounting.reports.read grants are accepted. Consult connection capabilities before requesting
reports.
const report = await client.getProfitAndLoss(connectionId, {
from_date: '2026-10-01',
to_date: '2026-10-26',
basis: 'accrual',
})Reports represent booked transactions, not unposted accruals. Net profit is not EBITDA. Group currency conversion and intercompany elimination must be performed separately.
Connections
List providers and their capabilities
Create a customer connect session
List connections in the key's environment
Read resource availability for this connection's scopes, state, and retention mode
Disconnect a connection
Queue reconciliation
Provider discovery returns each provider's availability, capability version, and supported
countries. Connect-session creation accepts a provider only when its adapter is installed, so
catalog discovery can never create a partially supported connection.
Create a connect session
Prop
Type
curl --request POST "https://api.accountingkit.dev/v1/connect-sessions" \
--header "Authorization: Bearer $ACCOUNTINGKIT_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"provider": "xero",
"external_account_id": "customer_123",
"redirect_uri": "https://app.example.com/integrations/xero/complete",
"retention_mode": "cached"
}'Resources
Replace :resource_type with organizations, contacts, invoices, quotes, payments,
purchase_orders, accounts, tax_rates, or branding_themes.
Paginated cached list
Live provider read
Live read by provider ID
Exact live search
Idempotent normalized create
Idempotent normalized update
Status-aware cancellation
Cancel from a stored provider ID
Organizations, tax rates, and branding themes are read-only. Contacts, invoices, quotes, payments,
and purchase orders accept normalized writes wherever the provider supports the operation. Accounts
are writable only through the provider-native body; a normalized account payload is rejected with
accounts is read-only in v1. See the provider-native Xero API for
lossless writes and search filters.
List queries support page, limit, status, number, and external_reference.
curl --request POST "https://api.accountingkit.dev/v1/connections/$CONNECTION_ID/invoices" \
--header "Authorization: Bearer $ACCOUNTINGKIT_API_KEY" \
--header "Idempotency-Key: invoice-order-10042" \
--header "Content-Type: application/json" \
--data '{
"external_reference": "order_10042",
"contact_id": "1f7c1e6c-1f9a-4b52-9f1e-2a4c6d8e0b31",
"status": "DRAFT",
"line_items": [{"description": "Implementation", "quantity": 1, "unit_amount": 1200}]
}'Automations and operations
Queue an idempotent recipe
Read durable execution state
Replay a failed or dead-letter operation
Automation creation returns 202. Poll until the state is terminal, or consume signed webhook
events instead. Operations lists every state and what it means.
Connection sync accepts {"scope": "invoices"} by default or {"scope": "all"}. Invoice scope is
the low-cost safety net: it compares live provider payment state, emits payment transitions, and
instructs consumers to refresh mutable invoice PDFs. Set "emit_invoice_snapshots": true on an
explicit sync to emit invoice.updated for every discovered invoice — intended for a controlled
consumer bootstrap. Scheduled reconciliation keeps it disabled and emits only meaningful payment
changes.
Webhooks
List endpoints in the key's environment
Register an endpoint, returning its secret
Remove an endpoint
List deliveries by state for recovery
Replay a failed customer webhook
The signing secret is returned once, at registration. Verification, the signature format, and the event list are covered in webhooks.
Provider actions
Run an explicit provider-native action
Supported actions are invoice.download_pdf, quote.download_pdf, purchase_order.download_pdf,
invoice.get_online_url, and organization.get_deep_link. PDF actions return application/pdf.
Every other action uses the standard JSON envelope.
Console-only endpoints
Provider-app management lives outside /v1 and requires a browser session. API keys deliberately
cannot manage OAuth credentials.
List apps in an environment
Create a resumable draft
Encrypt or rotate credentials
Check credentials, OAuth, webhook, and scopes
Draft creation takes environment, provider, name, and make_default, and returns the exact
callback URLs immediately. Credentials are submitted separately, so you can configure those URLs in
the provider portal before any secret is stored. Provider apps are isolated to one environment.
Required write headers
Every write needs an Idempotency-Key
Resource writes, cancellation, synchronization, automations, and replay all require an
Idempotency-Key of at most 255 characters. Responses carry Idempotent-Replayed: true|false, so
you can tell an original result from a replayed one. Keys are scoped to the organization and
environment and are retained for 30 days.