---
title: "API Authentication"
description: "Bearer tokens, login with two-factor, token lifetime, and what a permission error looks like."
url: "https://bareledger.app/guides/api-authentication"
category: "Developers & API"
updated: "2026-11-06"
publisher: "BareLedger (Syneria Technologies OPC)"
---

# API Authentication

> Bearer tokens, login with two-factor, token lifetime, and what a permission error looks like.

## Mechanism

**Bearer tokens** — not cookie sessions. The web and mobile clients obtain a token by logging in; third-party scripts and integrations use a user-bound **API key** instead (see [API Access](https://bareledger.app/guides/api-access)), which is the same bearer mechanism with a read or read & write scope. There is no OAuth client-credentials flow.

## Login endpoint

`POST /api/v1/auth/login` — email/password. Two outcomes:
- Success (no 2FA): returns a a bearer token immediately.
- 2FA enrolled: returns a challenge response instead; the client must then re-submit with a `totp_code` (or recovery code) to complete login and receive the token.

Google SSO is an alternate path to the same result — see Integration — Google SSO: the client ends up calling `POST /auth/google/exchange` to trade a short-lived exchange code for the same kind of bearer token (also TOTP-gated if enrolled).

## Using the token

Every authenticated request must send:
```
Authorization: Bearer <token>
```
and, for tenant-scoped endpoints:
```
X-Tenant: <tenant-uuid>
```
The `X-Tenant` value is only trusted if it matches one of the caller's own active memberships — a client cannot access another tenant's data by simply changing this header.

## Token expiration

Default token lifetime is **7 days**, after which the token stops working even without explicit revocation.

## Token refresh

There is no refresh-token endpoint — the client re-authenticates (login or SSO) to obtain a new token when the old one expires or is revoked.

## Logout / session management

- `POST /auth/logout` — revokes the current token.
- `GET /profile/sessions` — lists the caller's active tokens.
- `DELETE /profile/sessions/{tokenId}` — revokes a specific token (e.g. remote sign-out of another device).

## MFA (TOTP)

- `POST /auth/2fa/enroll` → `POST /auth/2fa/confirm` — two-step enrollment; a newly generated secret is staged as "pending" and only activated on `confirm`, so a bare `enroll` call can never silently disable existing 2FA.
- `POST /auth/2fa/disable` — requires re-verification (current password, or an emailed OTP for SSO-only accounts with no password).
- `POST /auth/2fa/recovery-codes/regenerate` — invalidates old recovery codes and issues new ones.
- Independent of login-time MFA, some sensitive actions (approval, posting, reversal, bank reconciliation completion) can require a **fresh TOTP code** again at the point of action, per tenant policy flags (`require_totp_for_review`/`_approval`/`_posting`, all default `true`). See [Authentication & Authorization](https://bareledger.app/guides/api-authorization).

## Permission enforcement errors

- No `X-Tenant` header, or it doesn't match an active membership → `409`.
- Valid tenant but missing the route's required `permission:<key>` → `403` with the required permission key in the response body.
- No active subscription (tenant billing lapsed) → `402` on write requests (reads and `*/billing/*` remain available).
- Plan limit reached → `402 plan_limit_reached` (user seats: invites, reactivations, onboarding approvals that create a login, employee invite-login) or `402 usage_limit_reached` (monthly transaction cap, returned by the approve/post step).
- Caller's own sign-in is beyond the plan's user limit after the 14-day grace → `402 seat_over_limit` on write requests (reads, `*/billing/*`, support, notifications and profile remain available).
- Tenant's country doesn't match a `country:<ISO2>` gate → `404` (not a 403 — the route is presented as not existing for that tenant).

See [API Error Reference](https://bareledger.app/guides/api-errors) for the full status-code catalogue.

## Rate limiting on auth endpoints

Login `20/min`, register `10/min`, password-forgot `6/min`, password-reset `10/min`, email-verification-related `6/min`, Google OAuth redirect/callback `20/min`, 2FA enroll/confirm/disable/recovery-regenerate `10/min` each. TOTP verification is additionally capped at 5 attempts/minute independent of the route throttle (tracked in `totp_attempts`). See [Security](https://bareledger.app/guides/security).
