API keys
A workspace API key is the credential your servers, scripts and integrations use to call the Jelliu REST API and the MCP server. This page is the operational deep dive: how a key is born, what each scope can and cannot reach route by route, how revocation propagates, how to rotate with zero downtime, and how to audit who did what with which key.
For the header format and the basic error responses, start with Authentication.
How it works
Every request passes three independent gates, in this order:
- Authentication. The bearer value must be
jl_followed by exactly 64 lowercase hex characters. Jelliu hashes it with SHA-256 and looks it up. Unknown, revoked or expired keys, and keys whose workspace was deleted, are refused with401. - Scope by HTTP method. Applied to every
/apirequest.GET,HEADandOPTIONSneedread,writeorfull.POST,PUT,PATCHandDELETEneedwriteorfull. - Role gate per route. Routes restricted to workspace roles check the key again. On routes that no ordinary member can use (admin, owner or billing only), only a
fullkey passes.
Some credential-management routes add a fourth gate that refuses every API key, including full. See Dashboard-only operations.
Three kinds of credential
Jelliu issues three unrelated credentials. They are not interchangeable, and each one is accepted only where listed.
None of these belongs in a browser, a mobile app bundle or a public repository. The only identifier that is safe to publish is a widget’s ID (a UUID), which the embed snippet uses.
The key object
GET /api/api-keys returns the workspace’s non-revoked keys, newest first. The secret is never included; only its public prefix.
Listing keys is an owner-level route. A full key can call it; read and write keys receive 403 with This operation requires an API key with the 'full' scope.
Lifecycle
Create
The workspace owner creates the key in the dashboard under Settings → API Keys. Admins cannot, and no API key can create another key.
The plaintext key is displayed once. Jelliu stores only its SHA-256 hash and cannot show it again. If you lose it, create a new key and revoke the old one.
Use
Send it on every request as Authorization: Bearer jl_.... Every key of the workspace shares the same rate limits, because limits are counted per workspace.
Expire (optional)
A key with an expires_at stops authenticating at that moment. Expiry is re-checked even when the validation result is cached, so a key does not keep working past its date. It stays in the list with usable: false until you revoke it.
Revoke
The owner revokes the key from the dashboard. Revocation is a soft delete (the record is kept for audit history) and frees a slot under the 25-key limit. The instance that handles the revocation evicts the key from its caches immediately; other instances stop accepting it within about 10 seconds. Revoking a key that is already revoked or does not exist returns 404 with API key not found or already revoked.
Keys also stop working when the workspace is deleted.
Limits on keys
Expired keys that have not been revoked still count toward the 25. Revoke them to free the slot.
What each scope can reach
The matrix below is derived from the gates applied to each route. “Member-level” routes are those a workspace member can use in the dashboard; “admin-exclusive” routes are those only an admin, owner or billing user can use.
A full key is treated as a workspace administrator and also passes owner-only routes: it can spend money on the saved card, read the audit log and erase contact data. Give it only to automations that genuinely manage the workspace, and store it like an admin password.
How a refusal looks
A key that lacks the method’s scope:
A read or write key on an admin-exclusive route:
Dashboard-only operations
These routes refuse every API key, whatever its scope, so a leaked key can never mint new credentials or switch off the controls meant to contain a leak:
The response is 403 FORBIDDEN:
Workspace sign-in policies do not apply to keys
If the workspace requires MFA or SSO for its members, that requirement applies to people signed in to the dashboard. API keys are machine credentials and pass through it, so hardening your members’ sign-in never breaks integrations.
Keys that belong to an agent
Jelliu also issues credentials for its own use: the tool gateway your voice and chat agents call during a conversation, and the short-lived access tokens of MCP OAuth connections. These credentials:
- are named with the reserved
internal:prefix, are hidden fromGET /api/api-keysand do not count toward the 25-key limit; - when bound to a specific agent, can never carry the
fullscope, and stop authenticating as soon as that agent is deleted; - for MCP OAuth, expire after one hour and never exceed the role of the person who approved the connection.
You cannot create, list or revoke them through the API. That is why agent_id is always null on the keys you manage.
Rotating a key without downtime
A workspace can hold up to 25 keys at once, and every key works independently, so rotation is a matter of overlapping two keys.
Create the replacement
In Settings → API Keys, create a new key with the same scopes. Give it a name that identifies the rotation, such as CRM sync (2026-09).
Deploy it everywhere
Update the secret in your secret manager and roll out every process that uses it. Both keys authenticate during this window.
Jelliu never forces rotation: a key without an expiry lives until you revoke it. If your security policy requires periodic rotation, set expires_at when you create the key and put the date in your calendar, then rotate before it arrives. An expired key fails immediately with 401, with no grace period.
If a key leaks
- Revoke it first, from Settings → API Keys. Do not wait until the replacement is deployed if the key was exposed publicly.
- Create a replacement and deploy it.
- Review what the key did. Every audited request records the key as
apikey:KEY_ID(see below). - If the key had
fullscope, also review outbound webhooks, WhatsApp templates, integrations and billing activity, which onlyfullkeys can change.
Auditing keys
Creation and revocation events
Creating and revoking a key each write an audit record with resource_type api_key and action api_key.create or api_key.revoke. The record carries the key’s public identity only: its id, prefix, scopes and expiry, who issued or revoked it, and, on creation, admin_equivalent: true when the key has the full scope. The secret and its hash are never written to the audit log.
The list omits the changes payload. Fetch one entry with GET /api/audit/{id} to see the prefix, scopes and expiry that were recorded. limit accepts 1 to 100 (default 50) and offset pages through older entries.
What a key did
Audited requests made with an API key are recorded with user_id set to apikey: followed by the key’s id. Filter on it to see a single key’s activity:
Alert on new keys in real time
Subscribe a webhook to audit.log_recorded with the auditActions filter set to api_key. to receive a signed event whenever a key is created or revoked. Remember that audit.log_recorded is not included in "*" and must be named explicitly. See Webhooks.
Errors
Security best practices
- Least privilege.
readfor reporting and BI,writefor integrations that add contacts or send messages,fullonly for automation that manages campaigns, webhooks, billing or compliance. - One key per integration and environment. Separate keys make
last_used_atmeaningful, let you revoke one integration without touching the others, and make the audit trail attributable. - Keep keys server-side. Never ship a
jl_key to a browser or mobile client. For a website chat, use the widget and its public widget ID. - Store keys in a secret manager, inject them as environment variables, and keep them out of logs. The 8-character
prefixis enough to identify a key in logs and tickets. - Watch for idle and expired keys. Periodically list keys and revoke any whose
last_used_atis old or whoseusableisfalse. - Alert on credential changes with an
audit.log_recordedwebhook filtered toapi_key.. - For AI assistants, prefer OAuth. A connector that signs in through MCP OAuth receives one-hour tokens tied to the approving person, instead of a long-lived key pasted into a config file.

