Authentication & Conventions
Everything shared by every endpoint: the base URL, versioning, how you authenticate, the error model, and the request conventions the whole API follows.
Base URL & versioning
Every route is served under a global api prefix and a URI version segmentv1. The one exception is the /health readiness probe, which is version-neutral and sits outside the prefix.
| Environment | Base URL |
|---|---|
| Production (current) | https://gulfpay-production.up.railway.app/api/v1 |
| Production (after DNS cutover) | https://api.grivaone.com/api/v1 |
So a controller route such as sales-invoices → GET / resolves to https://gulfpay-production.up.railway.app/api/v1/sales-invoices.
Authentication
The API is protected by a global JWT guard: unless a route is explicitly marked public, it requires a valid bearer access token. Obtain one from POST /auth/login, then send it on every request:
| Header | Value |
|---|---|
Authorization | Bearer <accessToken> |
Access tokens are short-lived. When one expires, call POST /auth/refresh (the refresh token travels in an httpOnly cookie or the request body) to obtain a fresh access token — no need to re-enter credentials. Live sessions can be listed and revoked under /auth/sessions, and accounts may be further protected with TOTP two-factor (/auth/2fa/*) or an SMS login code (/auth/phone-otp/*).
Public routes
401 without a valid bearer token. Public routes are labelled Public throughout this catalog.Errors
Errors use standard HTTP status codes with a JSON body. The common ones:
| Status | Meaning | Typical cause |
|---|---|---|
400 | Bad Request | Validation failed (a field is missing, malformed or out of range). |
401 | Unauthorized | Missing, expired or invalid bearer token. |
403 | Forbidden | Authenticated, but the role or company scope does not permit the action. |
404 | Not Found | The resource does not exist, or is outside your company scope. |
409 | Conflict | A uniqueness clash, or an Idempotency-Key replay whose payload differs from the original request. |
429 | Too Many Requests | Per-IP / per-account rate limit tripped (e.g. repeated login attempts). |
Conventions
Pagination
List endpoints accept page and limitquery parameters and return the newest records first. Additional per-module filters (date ranges, status, search) are shown in each endpoint’s notes.
Company scope — X-Company-Id
An organization can hold several companies (legal entities). Business endpoints are company-scoped: send the active company on the X-Company-Idheader so reads and writes are isolated to that tenant and company. Omitting it falls back to the caller’s default company where one applies.
Idempotency-Key
Write endpoints accept an Idempotency-Key header. Replaying a request with the same key returns the original result instead of creating a duplicate; replaying the key with a different payload is rejected with 409. This is what makes the offline write-sync queue safe to replay on reconnect.
Document numbering
Document and master codes are sequential per company, so numbering never leaks volumes across tenants.
Reading this catalog