# Getting started with the OmniManager API

> Authentication, the response envelope, permissions and rate limits shared by every /api/v1 route.

# Getting started

The OmniManager public API (`/api/v1/**`) gives programmatic access to a
tenant's account with a per-tenant API key: shops, WhatsApp connection and
sending, statuses, groups, and consent management — with the platform's
anti-ban policy enforced on your behalf on every send.

Base URL: `https://<your-omnimanager-host>/api/v1`

## Authentication

Create a key in **Settings → API** (requires `canEditSettings`), or ask an
OmniManager admin to mint one for your tenant. The raw key
(`omk_live_…`) is shown once; only its SHA-256 hash is stored, so if you lose
it you must rotate, not "recover" it.

Send it on every request:

```
Authorization: Bearer omk_live_YOUR_KEY
```

(`x-api-key: omk_live_YOUR_KEY` is accepted as an alias.)

A key can hold an expiry and an IP allowlist; revoking a key (from Settings or
the admin) takes effect immediately — the next request with that key fails
`401 API_KEY_REVOKED`.

## Permissions

Every route requires one permission from this list (`src/libs/api-keys/permissions.ts`);
a key only has the ones it was granted:

| Permission | Grants |
|---|---|
| `shops.read` | list/read shops |
| `shops.write` | create shops |
| `whatsapp.read` | read WhatsApp channel state |
| `whatsapp.connect` | start/poll/stop the WhatsApp connection |
| `messages.send` | send WhatsApp messages |
| `messages.read` | read message/delivery state |
| `statuses.post` | post a WhatsApp status |
| `contacts.read` | list declared (consented) contacts |
| `contacts.write` | declare / revoke consented contacts |
| `groups.read` | list the shop's WhatsApp groups |

`GET /me` needs none of these — a key with a single permission can still
verify itself.

## Envelope

Every response is one of two shapes:

```jsonc
// success
{ "success": true, "data": { /* … */ } }
// error
{ "success": false, "error": { "code": "DAILY_CAP_REACHED", "message": "…", "retryAfter": 3600 } }
```

Every response carries `X-Request-Id` (echoed back if you send one) and,
once authenticated, `X-RateLimit-Limit / Remaining / Reset / Scope` for the
bucket that request just consumed.

### Common error codes

| HTTP | code | meaning |
|---|---|---|
| 401 | `API_KEY_MISSING` / `API_KEY_INVALID` / `API_KEY_REVOKED` / `API_KEY_EXPIRED` | key problems |
| 402 / 429 | `QUOTA_EXCEEDED` | plan quota or capability spent |
| 403 | `IP_NOT_ALLOWED` / `TENANT_INACTIVE` / `FORBIDDEN_PERMISSION` | access denied |
| 404 | `SHOP_NOT_FOUND` / `MESSAGE_NOT_FOUND` / `DOC_ARTICLE_NOT_FOUND` | |
| 409 | `AMBIGUOUS_SHOP` | several shops share that name — pass the id instead |
| 409 | `WHATSAPP_NOT_CONFIGURED` | `GET` connect before any `POST` connect |
| 422 | `VALIDATION_ERROR` | body/query invalid (`error.fields`) |
| 429 | `RATE_LIMITED` | per-key minute/hour/day limit (`Retry-After`) |
| 429 | anti-ban policy codes | see [Messaging & the anti-ban policy](messaging-policy) |
| 503 | `BOT_SERVICE_UNREACHABLE` / `WHATSAPP_NOT_CONNECTED` / `WHATSAPP_SESSION_LOST` | session / bot down |

## Rate limits and metering

Per key, by default: 60/min, 1 000/h, 10 000/day (see `X-RateLimit-*`
headers on every response; a breach returns `429 RATE_LIMITED` with
`Retry-After`). Every call counts toward the plan's `api_calls` allowance,
visible in `GET /me`; every request is logged (route template, status,
latency, IP — never the request body) and visible to OmniManager admins.

## First call

```bash
curl -s -H "Authorization: Bearer omk_live_YOUR_KEY" https://app.omni-manager.com/api/v1/me
```

returns your key's permissions, rate limits and remaining `api_calls` quota.
