Authentication & keys
Every request to https://api.lojiq.ai/v1 carries an API key. The key identifies your organization; its scopes decide what it may do.
Sending the key#
Either header form works; pick one and use it everywhere. The bearer form is preferred.
curl "https://api.lojiq.ai/v1/leads" -H "Authorization: Bearer lojiq_live_5b7a…e3"
curl "https://api.lojiq.ai/v1/leads" -H "X-Lojiq-Api-Key: lojiq_live_5b7a…e3"
Keys look like lojiq_live_ followed by 64 hexadecimal characters (75 characters in total); keys of a sandbox organization start with lojiq_test_ and work the same way. Only a SHA-256 hash of the key is stored on Lojiq's side; the first 16 characters (lojiq_live_xxxxx) are kept in clear as a label so you can tell keys apart in the app. Anything that is not a key of this shape is refused as 401 invalid_api_key before any lookup.
Who can create keys#
Keys belong to an organization, not to a person. An organization Owner or Admin creates them in the Lojiq app at Developer → API keys; every creation and revocation is recorded with the user who did it. If you do not see the Developer page, ask your Lojiq contact to enable API access for your organization.
When you create a key you choose:
- name string required
- A label for your own reference (
hubspot-sync). Use one key per integration so you can revoke them independently. - scopes array of strings
- What the key may do (table below). An empty list or
["*"]grants everything. Grant the minimum. - rate_limit_per_minute integer
- Optional override of the default 60 requests/minute. Raising it is a request to your Lojiq contact.
- expires_at date-time
- Optional. After this moment the key answers
401 expired_api_key. Good for contractors and trials.
The full key is returned once, in the creation response. If you lose it, revoke it and create a new one, or use Rotate on the keys page: it issues a new key with the same name, scopes and limit and revokes the old one in the same step.
Scopes#
Scopes map one-to-one onto endpoint groups. Each reference page names the scope it needs, and 403 insufficient_scope tells you which one is missing.
| Scope | Unlocks |
|---|---|
leads:read | List, get and export leads |
leads:write | Create, update, bulk-import and inspect CSV |
contacts:read | List contacts |
contacts:write | Create contacts |
campaigns:read | List and get campaigns |
campaigns:write | Start and pause campaigns |
calls:read | List calls, transcripts, recordings, an agent's calls |
calls:write | Start calls (with an agent or ad hoc) and hang up |
agents:read | List and get voice agents |
agents:write | Create, update, delete agents; assign numbers |
voices:read | List voices and play previews |
account:read | Read your organization and voice_calls_allowed |
deals:read | List deals |
deals:write | Update deals |
appointments:read | List appointments |
appointments:write | Cancel appointments |
webhooks:manage | Event catalog and webhook subscriptions |
* or no scopes | Everything (full organization access) |
The four unauthenticated reference files (/openapi.yaml, /getting-started.md, /sdk/voice.js, /docs) need no key at all. GET / needs a valid key but no particular scope.
Organization scoping#
There is no organization_id parameter anywhere in the API. The key is the organization; an organization_id in a body or query is rejected like any unknown field. Ids that belong to another organization behave as if they did not exist (404 not_found, on reads and on updates alike). If one person works with several Lojiq organizations, they hold one key per organization.
Errors you will see#
| Status | error | Meaning |
|---|---|---|
401 | missing_api_key | No Authorization: Bearer and no X-Lojiq-Api-Key header. |
401 | invalid_api_key | The key is unknown (mistyped, or never existed). |
401 | revoked_api_key | Someone revoked it in the app. Create a new one. |
401 | expired_api_key | Past its expires_at. |
403 | insufficient_scope | Valid key, missing scope; required names it. Create a key with that scope (scopes cannot be edited after creation). |
500 | auth_lookup_failed | Lojiq could not check the key. Retry with backoff; if it persists, contact support with the X-Request-Id. |
Every one of these carries request_id in the body and X-Request-Id in the headers, like any other answer.
Keeping keys safe#
- Server side only. Never ship a key in a browser bundle, a mobile app or a public repository. Browser calls use the voice SDK with a short-lived
join_urlthat your backend obtains with the key. - One key per integration, named after it, with the narrowest scopes. Revoking one integration then never breaks another.
- Rotate by creating the new key first, switching the integration, then revoking the old one. Revocation is immediate.
- Watch
last_used_aton the keys page: a key that is still used after you thought you retired it is a finding. - If a key leaks, revoke it in the app right away. Then review
GET /webhooksfor subscriptions you did not create.
Sandbox keys#
Keys for the sandbox organization are issued the same way and work only against sandbox data. See Sandbox.