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.

bash
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.

ScopeUnlocks
leads:readList, get and export leads
leads:writeCreate, update, bulk-import and inspect CSV
contacts:readList contacts
contacts:writeCreate contacts
campaigns:readList and get campaigns
campaigns:writeStart and pause campaigns
calls:readList calls, transcripts, recordings, an agent's calls
calls:writeStart calls (with an agent or ad hoc) and hang up
agents:readList and get voice agents
agents:writeCreate, update, delete agents; assign numbers
voices:readList voices and play previews
account:readRead your organization and voice_calls_allowed
deals:readList deals
deals:writeUpdate deals
appointments:readList appointments
appointments:writeCancel appointments
webhooks:manageEvent catalog and webhook subscriptions
* or no scopesEverything (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#

StatuserrorMeaning
401missing_api_keyNo Authorization: Bearer and no X-Lojiq-Api-Key header.
401invalid_api_keyThe key is unknown (mistyped, or never existed).
401revoked_api_keySomeone revoked it in the app. Create a new one.
401expired_api_keyPast its expires_at.
403insufficient_scopeValid key, missing scope; required names it. Create a key with that scope (scopes cannot be edited after creation).
500auth_lookup_failedLojiq 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_url that 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_at on 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 /webhooks for 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.