Authentication
API keys, how to send them, scopes and presets, rotation, and keeping keys out of browsers.
3 min read
Every /v1 request authenticates with a bearer credential in the Authorization header. There are two kinds:
| Credential | Looks like | Use it when |
|---|---|---|
| API key | lsk_ + id + secret | Your own scripts and servers act on your workspace. |
| OAuth access token | lat_ + id + secret | Your app acts on behalf of other Listener users who connected it. See OAuth 2.0. |
A few endpoints need no credential at all: the API root GET /v1, the spec at GET /v1/openapi.json and the event catalog at GET /v1/event-types.
Sending the key#
Send the key as a bearer token:
curl https://api.listener.com/v1/me \
-H "Authorization: Bearer lsk_…"HTTP Basic auth with the key as the username and an empty password also works, which is handy with curl:
curl -u "lsk_…:" https://api.listener.com/v1/meKeys in the query string (?key=) are rejected on /v1: URLs end up in logs, browser history and proxies. The query-string form exists only on the MCP endpoint, for AI clients that cannot set headers.
Creating keys#
Create keys in Settings → Developers → API keys in either app: network.listener.com/app/developers or listener.com/app/developers. The page still answers at /app/mcp for older links.
When you create a key you choose:
- A name, shown in usage logs and in
GET /v1/meascredential.label. - Access: a preset or a set of fine-grained scopes (below).
- An expiry (optional), in days. Expired keys answer
401 credential_expired.
The full key is shown once. Listener stores only a hash, so a lost key cannot be recovered; create a new one. A workspace can have up to 10 active keys.
Presets and scopes#
Two presets cover most needs:
| Preset | Scopes | Good for |
|---|---|---|
| Read | workspace:read | Reporting, dashboards, exports, AI assistants that should only look. |
| Read & write | workspace:read workspace:write | Automation that creates campaigns, edits clusters or manages webhooks. |
For anything that runs unattended, prefer fine-grained scopes: a reporting job needs only analytics:read and shows:read; a campaign sync needs campaigns:read and campaigns:write. Each endpoint’s required scope is shown in the API reference, and a call without it fails with 403 scope_missing, naming the required_scope. The full list is in Scopes.
Rotating keys#
Rotate a key from the same page. Rotation issues a new secret immediately and keeps the old one working for 24 hours, so you can deploy the new value without downtime:
- Rotate the key and copy the new value.
- Update the secret wherever it is deployed.
- Watch the key’s Last used time; within 24 hours the old value stops working on its own.
Revoking a key is immediate. Do that if a key leaks, then create a replacement.
Keep keys out of browsers#
API keys carry the full authority of their scopes over a whole workspace and do not expire unless you set an expiry. Never embed one in front-end code, a mobile app, a browser extension or a public repository.
- Server-side only. Keep keys in environment variables or a secrets manager and call the API from your backend.
- Building a browser or mobile app? Use OAuth 2.0 with PKCE. Each user signs in with their own Listener account and you get short-lived, revocable tokens.
- Leaked a key? Revoke it in Settings → Developers right away; revocation takes effect immediately.
The API allows cross-origin requests from any origin so that OAuth apps running in a browser can use their access tokens. That is not an invitation to put lsk_ keys in client code.
Request logs#
Settings → Developers → Logs lists the last 14 days of requests per workspace (method, path, status, duration, which credential, request id). Bodies are never logged. Usage shows daily totals and errors per key for the last 30 days.