AccountingKitDocs

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.

StatusCodeWhen it happens
400BAD_REQUESTContract validation failed, or the request is malformed.
401UNAUTHORIZEDThe API key is missing, invalid, or revoked.
403FORBIDDENThe key is valid, but your plan does not allow this action or another connection.
404NOT_FOUNDThe connection, resource, or operation does not exist for this key.
409CONFLICTIdempotency conflict, a disallowed provider transition, or a revoked connection.
413PAYLOAD_TOO_LARGEThe request body exceeds 10 MB.
429RATE_LIMITEDThe connection's provider request budget is temporarily exhausted.
500INTERNALAn unexpected failure. Safe to retry with the same idempotency key.
503UNAVAILABLEThe 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

GET/v1/connections/:connection_id/reports/profit-and-loss

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

GET/v1/providers

List providers and their capabilities

POST/v1/connect-sessions

Create a customer connect session

GET/v1/connections

List connections in the key's environment

GET/v1/connections/:connection_id/capabilities

Read resource availability for this connection's scopes, state, and retention mode

DELETE/v1/connections/:connection_id

Disconnect a connection

POST/v1/connections/:connection_id/sync

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.

GET/v1/connections/:connection_id/:resource_type

Paginated cached list

GET/v1/connections/:connection_id/:resource_type/:resource_id

Live provider read

GET/v1/connections/:connection_id/:resource_type/by-provider-id/:provider_id

Live read by provider ID

POST/v1/connections/:connection_id/:resource_type/search

Exact live search

POST/v1/connections/:connection_id/:resource_type

Idempotent normalized create

PATCH/v1/connections/:connection_id/:resource_type/:resource_id

Idempotent normalized update

POST/v1/connections/:connection_id/invoices/:resource_id/cancel

Status-aware cancellation

POST/v1/connections/:connection_id/invoices/by-provider-id/:provider_id/cancel

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

POST/v1/automations

Queue an idempotent recipe

GET/v1/operations/:operation_id

Read durable execution state

POST/v1/operations/:operation_id/replay

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

GET/v1/webhook-endpoints

List endpoints in the key's environment

POST/v1/webhook-endpoints

Register an endpoint, returning its secret

DELETE/v1/webhook-endpoints/:webhook_endpoint_id

Remove an endpoint

GET/v1/webhook-deliveries

List deliveries by state for recovery

POST/v1/webhook-deliveries/:delivery_id/replay

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

POST/v1/connections/:connection_id/provider-actions/:action

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.

GET/accounting/provider-apps

List apps in an environment

POST/accounting/provider-apps

Create a resumable draft

PUT/accounting/provider-apps/:provider_app_id/credentials

Encrypt or rotate credentials

POST/accounting/provider-apps/:provider_app_id/verify

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.

On this page