Authentication and API keys
Create, scope, send, rotate, and revoke Anectico credentials safely.
On this page
Human Console sessions and programmatic API keys are separate credentials. Use API keys for SDKs, collectors, CI, CLI automation, and MCP clients. Public capture and OTLP use separate purpose-bound native intake keys; management credentials cannot enter those routes.
OAuth for connected agents
An OAuth-capable MCP host can connect through sign-in and workspace consent. It uses S256 PKCE
and the MCP URL as its resource. Registration supports public clients, including clients that
omit token_endpoint_auth_method; the response selects none. Client metadata documents must
be reachable at a public HTTPS URL, identify that same URL, fit within 5 KiB, and avoid redirects.
OAuth permits reads only. Requests for write scopes return invalid_scope. Writes require an
owner-created API key with the required permissions. See Connect MCP.
Refresh tokens rotate. An identical retry within five seconds receives the same rotation;
a simultaneous request may return 503 temporarily_unavailable with Retry-After: 1. Retry
after that delay. Reusing a consumed refresh token after five seconds revokes its approval and
requires fresh consent. Other approvals stay connected.
To stop an approval, use Agents and keys or your human CLI login. There is no advertised OAuth token revocation endpoint. See Revoke agent access.
Create a key
An agent connected through OAuth cannot create a key, because OAuth permits reads only. An agent
with an owner-created management API key that holds mcp:write, api_key:write and every scope the
new key carries can create one when you ask, with the CLI (anectico apikey create) or the MCP write
actions create_intake_key for intake and create_api_key for management, server application
or client-delivery credentials. Each is created only after you approve its complete preview,
and a telemetry or capture key is bound to one exact project. The secret goes into the application's environment or
secret store, never into source code, a log or a transcript you share. To create a key yourself,
open Agents and keys in the Console (/agents), then API Keys. Select Telemetry (OTLP),
Browser capture, Mobile capture, Server capture, or Management / agent,
then choose a project. Intake purposes have one fixed permission: ingest:write for
telemetry, analytics:write for capture. Only management keys offer read or full presets
and custom permissions; management keys cannot send public capture or OTLP.
An optional existing source ID must match the selected project and intake purpose. Leave it empty to use that project's default source for the purpose. The list shows each key's purpose and source. Editing cannot change purpose, project or source; Allow intake can disable or restore a native key's single permitted scope.
Choose how long the key should remain valid. Never is the default; fixed choices from one day through one year create a key whose expiry is shown with its metadata. Expiry cannot be extended after creation. The secret is shown once and is never stored in browser storage.
After a connection failure, Retry same request keeps the original arguments and expiry. An already committed request returns metadata only; revoke that key in the list before creating a replacement if you lost its secret. Closing the form or leaving the page discards the in-memory retry state, so check the key list before starting again. Project, session and permission changes also clear displayed secrets and pending form state.
You can also create keys through the account API, CLI or MCP; see native intake keys. Creating a key in the Console supplies a retry key automatically. API and CLI retry keys are optional; MCP creation requires one.
The account API accepts the same optional expiry as an RFC3339 timestamp. Generate the expiry immediately before submitting the request rather than copying a fixed calendar date. With Node.js installed, this prints a one-day JSON payload; it does not create a key or send a request:
node <<'JS'
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
console.log(JSON.stringify({
name: 'One-day CI key',
scopes: ['traces:read'],
expires_at: expiresAt,
}, null, 2));
JS
Send the resulting JSON to POST https://app.anectico.com/api/v1/account/api-keys using an
existing credential with api_key:write; it can grant only scopes the caller is allowed to grant.
Choose the intended project_id as described below; omitting it does not create an
organization-wide key. Do not commit or log the returned plaintext key.
Omit expires_at for a key that never expires. A supplied timestamp must be in the future and no
more than 365 days from creation. An expired key is rejected on its next use; revocation remains
immediate and permanent.
Owner work limits live in a separate policy. Edit Agent budget beside the management key or
use anectico budget set --file budget.json. Queries, writes, destructive writes and exports have
hourly and daily limits, plus concurrency. See Manage agent budgets.
Anectico keys begin with an_. Treat the rest as opaque; do not parse meaning from it.
Send an API key
The public REST API accepts:
X-Anectico-API-Key: an_...
It also accepts the generic X-API-Key header and Authorization: ApiKey an_.... MCP clients
normally use:
Authorization: Bearer an_...
Use the header documented by the SDK or client you are configuring rather than sending multiple credential headers.
Common key recipes
Scopes use resource:action names.
| Purpose | Required starting scopes |
|---|---|
| OTLP traces, logs, metrics, and captured errors | purpose: native_ingest, only ingest:write |
| Product capture, identity and group controls | purpose: browser_capture, mobile_capture or server_capture, only analytics:write |
| Browser session replay | replay:write |
| Evaluate feature flags on your server and record exposures | Management flags:read key held on the server; separate capture key for exposures |
| Evaluate named feature flags from your page or app | purpose: client_delivery, only flags:evaluate; exposures use a separate capture key |
| Assign people to product experiments and record exposures and outcomes (your application server) | purpose: application, only experiments:deliver |
| Show in-product surveys from your page or app and record answers | purpose: client_delivery, only surveys:respond |
| Upload native replay chunks from your app | purpose: replay_upload, only replay:write |
| Upload source maps, ProGuard mappings, or dSYM binaries | errors:write |
| Register a release in CI | releases:write |
| Read through MCP | mcp:read plus the required domain read scopes |
| Read documentation through MCP | mcp:read and docs:read |
| Allow MCP writes | mcp:read, mcp:write, and the required domain-specific scopes |
| Read service health from an app you host, such as a status board | A dedicated management key for that app, bound to one project, only services:read, with a fixed expiry. See the key for an app you host |
| Search logs | logs:read |
| Read traces | traces:read |
| Read or triage Issues | errors:read or the required error write scope |
| List the organization's API keys | api_key:read |
| Create, rename, revoke, or re-scope API keys | api_key:write |
mcp:read is an umbrella, not permission to read every domain. For example, an MCP trace reader
also needs traces:read; a docs-only agent needs docs:read instead. A write scope does not imply
its matching read scope. URL profiles may further hide capabilities, but cannot grant missing
scopes. Start with agent scope recipes and check
the agent security model before granting writes.
Use distinct credentials for capture, OTLP and management APIs. A native key cannot gain replay, flag-read or management scopes, and cannot exchange for a management bearer token. Choose the explicit intake purpose when generating a key; scope names alone do not grant intake authority. See the supported SDK setup paths in native intake keys.
An application key (purpose: application) is a secret for your application server. It is
bound to one project, carries only experiments:deliver and cannot be widened; no member role and
no ordinary key can hold that scope. Only an owner or admin (who holds experiments:launch) can
create one. It assigns identified people to product experiments, registers the first exposure and
records outcome receipts for its own project — nothing else. It is secret authority, not proof that
a request came from server hardware: never ship it in browser or mobile code, and revoke it if it
leaks. Record the outcome event itself with a separate server_capture key. The word
"application" here means server-side experiment delivery. It is not the key for an app that reads
your data: it reads nothing, and an app that reads uses the management key described next.
A client delivery key (purpose: client_delivery) is the public key you place in your own
page or app build, where anyone who installs or opens it can read it. It is bound to one project,
carries no intake purpose and holds only flags:evaluate, surveys:respond or both, chosen
explicitly when you create it. It can list no flag or survey, read no targeting, stored property or
event, change nothing and mint nothing; its purpose and project never change, and a scope edit can
only stay within those two scopes. Requests made with it are your software's traffic, never agent
activity. You create it with the existing key creation flow, and you need api_key:write plus each
scope you grant (members hold both). Issue one key for surveys and a separate one for flags unless
one build needs both:
anectico --project PROJECT_ID apikey create --purpose client_delivery --scope surveys:respond \
--name "web app surveys" --expires-in 90d
Client delivery keys authenticate only POST /api/v1/flags/evaluate,
POST /api/v1/surveys/eligible, and the shown, responses and dismissals POST routes beneath
/api/v1/surveys/{surveyId}. Flag exposure events use a separate capture key. Experiment delivery
and its exposure and outcome receipts use the separate server application key described above.
A replay upload key (purpose: replay_upload) is bound to one project and carries only
replay:write. It authenticates only POST /api/v1/replay/chunks; upload still requires current
native replay settings and independent storage permission. It cannot read or delete a replay.
Both software purposes are refused on every other authenticated endpoint, including MCP,
streaming logs, account and session controls, the scope catalog and authenticated documentation.
The response is HTTP 403 with error: permission_denied and a fixed purpose message. Exchanging
a key for a signed token does not widen its reach. Public discovery and independently authorized
public protocols grant no authority from either key.
Browser app-view reads use their own credential scheme. They reject ordinary Authorization
credentials with 401 access_ended; credential-bearing app-view preflights are refused with 403.
API-key headers on either method are refused with 400 invalid_request.
Use a separate stable --idempotency-key for each proposal. The command prints the full proposal
(purpose, project, scopes and fixed expiry) and a confirmation token; repeat it with --confirm-token, --idempotency-key and the printed --expires-at to
create the key. The secret is shown once; a retry returns the metadata without it. A key created
with surveys:respond works on the survey delivery routes. A key created with flags:evaluate works on
POST /api/v1/flags/evaluate, which returns decisions for the 1 to 100 flag keys the app names and
never a definition:
anectico --project PROJECT_ID apikey create --purpose client_delivery --scope flags:evaluate \
--name "mobile app flags" --expires-in 90d
Client-side feature flags use that key and nothing broader. Never put a flags:read key in a page or
app: it also reads every flag definition, targeting rule and payload of the project. Exposures still
go through a separate capture key. An agent uses its own management credential to configure and verify; public client
evaluation uses the separate delivery key. The existing server integration workflow does not wire flags into client code
and does not qualify mobile delivery. Native source APIs and their pending runtime qualification
are in mobile setup. See Feature flags from a page or app.
The Access screen is the current source for the complete scope catalog. See the permission scope reference for the selectable groups and safe key patterns.
Which credential each host holds
A program your agent writes against the API holds one credential, and where the program runs decides which one. You host the program and keep its secret; Anectico supplies the API and the credentials. Build an app on the REST API says what such a program is.
| Host | Credential it holds |
|---|---|
| Your backend or an internal tool | A dedicated management key for that app, bound to one project, in the host's secret store. |
| A browser page, or a page in a chat host | None of Anectico's. It calls your own fixed, authenticated backend API. No management key appears in the page's assets, storage, network traffic or address. |
| A mobile app | No management key. Reads of protected data go through your backend. Its SDK uses separate public native-ingest and mobile-capture credentials, plus explicitly configured client-delivery keys for flags and surveys. |
| A Slack bot | A secret on the bot's server. That server authenticates and authorizes each workspace and each user's command itself. |
| A scheduler (cron) | A dedicated least-permission key in the scheduler's secret store, with a fixed project and explicit query windows. |
| Public telemetry | The existing capture, replay or survey key, for the matching SDK operation only. |
| A browser reading an approved saved aggregate directly | A temporary app_read token minted by its exact backend parent; at most five minutes, one approved result and projection, exact origins, no refresh. Recipe. |
A read-only management key is never public. It cannot write, but a project-wide read discloses your customers' data, and CORS does not protect it: CORS decides whether a browser may read a response, not whether the request runs. Do not put one in a page, a mobile binary, a repository or a log. The key is not limited to named routes, resources or origins, so your backend enforces which of its own users may see which route, resource, project and field.
Three other credentials look like candidates and are not:
- The application purpose is server-side experiment delivery under
experiments:deliver. It reads nothing, so it is not a key for an app that reads your data. - A capture, replay, survey or telemetry key is for sending data from its SDK. It cannot read. A client delivery key is the survey and flag evaluation key placed in an app; it cannot read your data either.
- OAuth is how your agent discovers and investigates through MCP while it builds the app. It is not a way to give an app, or a browser, access.
The key for an app you host
Give each deployed app its own key, so that you can see exactly what it holds and stop it alone.
- Name. Name it for the app, such as
status-board. - Project. Bind it to the one project the app reads. The binding is fixed for the life of the key.
- Permissions. Grant only what the app calls. A service-health board needs
services:readand nothing else: notmcp:read(MCP is how your agent works with Anectico, not how the app reads), notapi_key:write, and nothing for billing, members, connections or privacy. Do not use a bundle such asinvestigate. Add a permission that releases customer content (persons:profile:read,replay:content:readoragents:content:read) only when the app must show that content. - Expiry. Choose a fixed expiry in the future and replace the key before it arrives.
- Limits. The key has its own operation budget and request rate, set under Agent budget beside it on Agents and keys. They slow or block the key. They do not end it.
The worked examples list the exact permissions each kind of app needs, and Give an app its own key is the checklist. Your agent provisions the key and you confirm it:
anectico --project PROJECT_UUID apikey create --name status-board --scope services:read \
--expires-in 90d --idempotency-key status-board-1
# Review the complete server preview, then repeat the arguments with the fixed expiry it printed:
anectico --project PROJECT_UUID apikey create --name status-board --scope services:read \
--expires-at <expiry> --idempotency-key status-board-1 --confirm-token <server-token>
The secret appears once, in the first successful response. It is not in the audit log, the activity
record, a retry or any later read. If that response is lost, repeat the same command with the same
--idempotency-key. You get the original key's details and no secret, and no second key is created. If
you never saw the secret, revoke that key and create a new one with a new --idempotency-key.
The credential that provisions the key is the builder's own, not the app's. It holds api_key:read
and api_key:write, plus mcp:write for the preview and confirmation, plus every permission it
grants. An app's runtime key never creates, lists, edits or revokes keys: those are customer controls,
and every route that does them refuses it.
What the key means. The key identifies your application, not your application's users. Anectico records each call under the key's name, and your backend decides which of its own users may see what and records who asked. Do not send your users' session tokens or ids to Anectico as if it had verified them.
A page that shows a saved result to its visitors
A management key never goes into a browser. For a page that must read Anectico directly, an owner creates
an app-view grant instead: a permission, separate from every read, for one project's key to mint
short-lived browser tokens that show exactly the saved trend results the grant names, through a fixed set
of aggregate fields, to exact https:// page origins, until a fixed expiry of at most 90 days.
- What a grant can show. Counts, values, coverage and window facts of a saved trend result without a breakdown. Never the people, rows, labels, definition, filters or participant lists behind it; never a query run, a write or anything else.
- Who can create one. An owner or admin, or a management key an owner gave
app_views:sharetogether withapi_key:readandapi_key:write. The key a grant names as its data source holdsapp_views:mint,analytics:read,persons:readand the results' own permissions. - What stops it. Revoking the grant, revoking or expiring that key, removing a permission from it, the grant's own expiry, or turning sharing off in the project's content policy. A grant cannot be widened: a change is a new grant and a revocation of the old one.
- What it cannot undo. Numbers a visitor already saw or copied.
Use app-view grant list/create/revoke or the MCP gateway actions list_app_view_grants,
create_app_view_grant and revoke_app_view_grant. Creation and revocation require the server's complete
reviewed confirmation and state fence. Owners review every grant on Agents and keys; sharing
consent is on Privacy. Token mint is a separate backend and CLI protocol with a required private
--out, never a normal MCP token-read action. Read the complete
browser aggregate recipe, including theft and external-copy limits.
Stop an app
Revoke the app's key. It is the one action that stops the app's next request, a read included. Open
Agents and keys, or run anectico apikey revoke. Two other actions look like stops and are not:
- Freeze removes a key's write permissions and keeps its read permissions. A key that only reads keeps reading. An agent-level kill reaches only keys that declared which agent they belong to, and an app's key normally declares none.
- A limit of zero, on operations or on the request rate, makes the key's requests answer
429. The key is still valid and you can lift the limit. It is a throttle.
Revoking does not stop the program that holds the key, retract data it already read, or undo an effect it already caused. Its caches, exports, logs and messages are yours to delete; Anectico's erasure cannot reach them. See Revoke an agent's access.
Project scope
A project-scoped key may omit a query-string project_id or repeat its exact signed project. A
different non-empty value returns 403 permission_denied before product logic runs; Anectico never
silently substitutes the signed project for an explicitly conflicting selection. Organization-level
keys retain request-time project selection, and many REST reads accept project_id as a query
parameter.
A key created without a project selection is bound to the organization's default project. Creating
an organization-level key is an explicit choice: send "org_wide": true in the create-key request
(the CLI's anectico agent bootstrap --org-wide does this), and the key is minted with no project
binding at all. This is available only for management keys; native intake keys always require a project.
The project binding is fixed for the life of a key. To move a workload to another project, create a replacement key for that project, deploy it, verify traffic, and revoke the old key.
That binding is also an authority floor when one API key creates another. A project-scoped key with
api_key:write can create a successor only in its own project; requesting org_wide or naming a
sibling project returns 403 forbidden. Omitting the project keeps the creator's project —
it never widens the successor because the default project cannot be resolved.
Scope edits, expiry, and revocation take effect on the next request because the
API key's authorization is resolved server-side for every request, whether it arrives as telemetry
or as an API call; successful checks are not cached. If that authorization check is unavailable,
API-key OTLP and Capture requests fail closed with a retryable service-unavailable response. Removing traces:read, logs:read,
metrics:read, errors:read, search:read, persons:read, or groups:read immediately makes the
corresponding Query API return 403; a native OTLP key continues to ingest only while its source is active and ingest:write remains present.
Every native key is bound to an intake source (see ingestion sources and pipelines),
and revoking that source stops every key bound to it immediately, independent of the key's own
expiry or scopes. The refusal is explicit and named the same way everywhere: an an_ capture key
refused for this reason gets 403 forbidden with an X-Anectico-Refusal-Reason: INGRESS_SOURCE_REVOKED
response header; OTLP over HTTP returns 403 with the message intake source revoked; OTLP over
gRPC returns PermissionDenied with the same message. A project's default source (the one used
implicitly when a key omits source_id) is never recreated once revoked — the project keeps that
purpose's default permanently unavailable until a new source is created explicitly and new keys are
minted against it with source_id.
Rotate and revoke
Create the replacement key, deploy it, confirm traffic uses it, then revoke the old key. Revoke immediately if a key is committed, logged, included in a screenshot, or exposed to an untrusted client.
CLI key creation, updates and revocation first print the complete server preview. Apply only by repeating identical arguments with its --confirm-token and the same --idempotency-key. Copy any generated name and fixed expiry from the preview. Key secrets are returned once and never included in activity records or retries.
Publishable static-page authority
A static aggregate page deliberately publishes
one finite app_publishable credential, delivered once by confirmed grant
creation with publishable_result_id. It uses only Authorization: AppPublishable
on the exact published aggregate route and expires within 30 days. Anyone
copying it can read that projection with a forged Origin. It authenticates no
ordinary REST, MCP, CLI, cookie, Live or credential-exchange operation. Keep its
parent management key secret; revoke the grant or parent to stop future reads.
Native replay upload authority
Use a separate project-bound replay_upload purpose carrying exactly replay:write for native
structural uploads. It has no intake purpose and cannot read recordings, upload browser DOM,
change configuration, use MCP or exchange into a management bearer. Its software uploads are
excluded from agent activity and agent budgets by their signed purpose. The application also
needs its separate mobile configuration credential.
An authorized agent provisions it through existing API-key management and must hold literal
replay:write for the same active project plus api_key:write (and mcp:write for the confirmed
MCP flow). Preview the exact purpose, project and scope, confirm once and preserve the creation retry
key after uncertainty. An organization-wide key, wildcard or extra scope is refused. This
publishable software key is not device attestation or proof of a person's consent. Native
recorder and reader support still requires qualification.