System-wide conventions that apply across every endpoint in API & Developer Documentation.

Base URL & versioning

All endpoints are under /api/v1/ — e.g. https://bareledger.app/api/v1/journals. Versioning is a URL path segment (v1); there is no header-based or content-negotiation versioning scheme. There is currently only one version (v1) — no deprecated/parallel version exists.

Organization scope

The API only ever shows you the organizations you are registered in. GET tenants lists your own memberships; every tenant-scoped endpoint reads and writes the single organization named in the X-Tenant header, which must be one of those memberships (409 otherwise). Records belonging to any other tenant are invisible — lookups return 404, never 403, so their existence is not confirmed.

Content type

Requests and responses use application/json throughout, except file uploads (attachments, avatars, statement imports), which use multipart/form-data.

Date & time format

Dates/timestamps are ISO 8601. Money is always transmitted as a decimal string or number with up to 4 decimal places for ledger amounts (2 for platform billing amounts) — never as a floating-point approximation; treat monetary fields as exact decimals in client code, not native floats.

Timezone behavior

The backend stores instants in UTC; the tenant's configured timezone (and, for accounting periods, its fiscal_year_start_month) governs how due dates, period boundaries, and recurring-schedule occurrences are calculated. Branches can override the tenant's timezone individually. See Compliance, Tax & E-Invoicing for the timezone-sensitive recurring-document generation logic.

Pagination

Every list endpoint except the chart of accounts can return one page at a time. Pass page (and optionally per_page, default 25, maximum 100), plus search for a case-insensitive text match and sort with dir (asc or desc) to order the whole list. The response carries a meta object:

{
  "data": [ /* ... */ ],
  "meta": { "current_page": 1, "last_page": 6, "per_page": 25, "total": 137 }
}

Leave out page and per_page to get every matching row in data with no meta, which suits dropdowns and full syncs. A few lists that grow without limit — the audit trail, notifications, leads, CRM activities and the document inbox — always return a page, starting at page 1 when you don't name one. Keep requesting the next page until current_page reaches last_page.

Filtering & searching

Most list endpoints accept relevant filters as query parameters (e.g. status, contact_id, date ranges) — see each resource's own API page for its specific filters. A cross-module GET /search endpoint (see API — Audit & Meta) provides a global search, with per-module results gated by that module's own view permission (Customers & Suppliers has no gate, so contact results are visible to every member).

Sorting

Sorting parameters, where supported, are documented per-endpoint on the relevant API Endpoints page — there is no single global sort-parameter convention confirmed across every list endpoint. Implementation requires verification if you need a guaranteed sort order on a specific endpoint not otherwise documented.

HTTP status codes

See API Error Reference for the full table (200/201/204, 400, 401, 402, 403, 404, 409, 422, 429, 500) and what each means specifically in this system — several are used in non-obvious, deliberate ways (e.g. 404 for cross-tenant records instead of 403, to avoid confirming existence; 402 specifically for lapsed-subscription lockout; 202 for pending change-request writes).

Validation response format

422 responses follow this shape:

{
  "message": "The given data was invalid.",
  "errors": {
    "field_name": ["A human-readable validation message."]
  }
}

Validation rules are documented per-endpoint on the relevant API Endpoints page where discoverable, and marked Implementation requires verification where the exact rule set wasn't fully confirmed.

Error response format

Non-validation errors generally return {"message": "..."}, with 403 responses additionally including the specific missing permission or feature key where relevant (see API Error Reference).

Rate limits

A global default of 300 requests/minute per authenticated user (or per IP if unauthenticated), plus tighter limits on specific sensitive routes (login, registration, password reset, 2FA actions) — see Security for the full table. Exceeding a limit returns 429 Too Many Requests.

Idempotency

There is no generic client-supplied Idempotency-Key header mechanism across all endpoints. Idempotency is instead built into specific operations at the domain level: posting a journal/document twice is a no-op (not a duplicate post), billing webhook events are deduplicated by (provider, event_id), and bank statement imports deduplicate by a content hash / bank-provided transaction id. See Journal Entries & Posting Engine and Subscription & Billing for the specifics.