Authentication
People use the console with a session cookie. Programs use API keys, either directly or exchanged for a one-hour access token. Every request acts in one organization and is checked against a role.
Console sessions#
Signing in with POST /api/v1/auth/login sets the oqim_session cookie: HTTP-only, SameSite=Lax, Secure in production, and valid for 14 days by default (SESSION_TTL_HOURS). The browser sends it with every request to your Oqim host; your code never handles it. The server keeps only a SHA-256 hash of the session token.
- A session belongs to one user and has one active organization. Switch with
POST /me/switch-organization. - If the user has two-factor authentication on, sign-in answers
{"mfa_required": true}. Until a code is posted toPOST /auth/mfa/verify, the session can only call that endpoint,GET /meandPOST /auth/logout; everything else returns401 mfa_required. - Requests that change data with a cookie must come from an allowed origin (
ALLOWED_ORIGINS), or they fail with403 origin_rejected. Requests with a bearer credential are exempt, because browsers never attach one on their own. - Disabling a user ends their sessions immediately.
Scripts and integrations should use API keys rather than console sessions.
API keys#
Owners and Admins create keys in the console under API & webhooks, or with POST /api-keys. A key looks like this:
It's oqk_, a 12-character prefix Oqim uses to find the key, an underscore, and the secret. The full key is shown once, when it's created; Oqim stores only its SHA-256 hash, and the console shows the prefix so you can tell keys apart. Send it as a bearer token:
- A key belongs to one organization and acts with the
API_CLIENTrole (see below). It can't use the console, switch organizations or call the administration API. - Keys can expire: pass
expires_in_dayswhen creating one. Oqim records when and from which IP each key was last used. - Keep keys on servers. Anyone holding a key can act as your organization within its permissions.
Access tokens#
If you'd rather not send the long-lived key on every request, for example from a job runner that logs request headers, exchange it for an access token that lasts one hour:
- Send
access_tokenasAuthorization: Bearer oqat_…, exactly like a key. It has the same permissions as the key it came from. - Tokens can't be refreshed. Request a new one before
expires_at. - Every request re-checks the key behind a token, so revoking the key invalidates its tokens at once.
/auth/tokenis a public endpoint limited to 30 requests per minute per IP. Reuse a token for its whole hour instead of requesting one per call.
Rotating and revoking keys#
Rotate a key in the console or with POST /api-keys/{id}/rotate. The response contains the new key, shown once. The old key keeps working for 24 hours so you can deploy the new one, then stops. The new key's rotated_from names the key it replaced.
If a key may have leaked, revoke it with DELETE /api-keys/{id} instead. Revocation is immediate and also ends every access token issued from that key.
Roles and permissions#
Every membership has one role, and every API key has API_CLIENT. The API checks permissions, never role names; each endpoint in the API reference lists the permission it needs.
- A request without the needed permission fails with
403 forbidden, and the message names the missing permission. - While an organization is suspended, every
*:managepermission andcampaigns:executeare refused with403 organization_suspended. Reading still works. - Only Owners can make someone an Owner or remove one; Admins manage every other role.
- API keys can import recipients and run campaigns, but connecting Telegram accounts, managing the team and managing keys always take a person in the console.
Two-factor authentication#
Anyone can turn on TOTP two-factor authentication under Settings → Profile & security. Under the hood:
Enroll
POST /me/mfa/setup returns a secret and an otpauth:// URL to show as a QR code. Nothing changes until POST /me/mfa/enable confirms a first code. Codes have 6 digits, change every 30 seconds, and one step of clock drift is accepted. The secret is stored encrypted.
Platform administrators
The admin console refuses administrators without two-factor authentication (mfa_enrollment_required) and asks for a code in every new session. See Administration.