---
title: "API Conventions"
description: "Base URL, pagination, dates and timezones, filtering, status codes, validation errors, and rate limits."
url: "https://bareledger.app/guides/api-conventions"
category: "Developers & API"
updated: "2026-10-24"
publisher: "BareLedger (Syneria Technologies OPC)"
---

# API Conventions

> Base URL, pagination, dates and timezones, filtering, status codes, validation errors, and rate limits.

System-wide conventions that apply across every endpoint in [API & Developer Documentation](https://bareledger.app/guides/api-overview).

## 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](https://bareledger.app/guides/compliance) 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:

```json
{
  "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](https://bareledger.app/guides/api-audit)) 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](https://bareledger.app/guides/api-overview) 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](https://bareledger.app/guides/api-errors) 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:
```json
{
  "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](https://bareledger.app/guides/api-overview) 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](https://bareledger.app/guides/api-errors)).

## 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](https://bareledger.app/guides/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](https://bareledger.app/guides/journals) and [Subscription & Billing](https://bareledger.app/guides/billing) for the specifics.
