# Native intake keys

> Create separate project and source credentials for product capture and OTLP telemetry.

Canonical page: https://anectico.com/docs/instrument/native-intake-keys/


Public product capture and OTLP require purpose-bound API keys. A management key, Console
sign-in session or bearer principal token cannot send telemetry, even when it has a write scope.

## Choose the route

| Key purpose | Route | Only permitted scope |
| --- | --- | --- |
| `browser_capture` | `POST /api/v1/capture` | `analytics:write` |
| `mobile_capture` | `POST /api/v1/capture` | `analytics:write` |
| `server_capture` | `POST /api/v1/capture` | `analytics:write` |
| `native_ingest` | OTLP HTTP `/v1/traces`, `/v1/logs`, `/v1/metrics`, or OTLP gRPC | `ingest:write` |

All four require one project and an active source. The server selects a stable default source
for each project and purpose when `source_id` is omitted. To rotate within a source, reuse its ID.
A source cannot change purpose or move to another project. A revoked default is not recreated.
Browser and mobile capture record the credential's declared purpose; neither proves the caller's
device or that an event represents a real person. Mobile capture uses `mobile_product` origin,
not trusted server origin. A key distributed in an app is a public client credential.

## Create keys in the Console

The setup guide creates a telemetry key and a separate capture key for your selected platform. In
the Console, open **Home**, then **Open the setup guide** (`/get-started`). Choose your platform:
browser uses `browser_capture`, mobile uses `mobile_capture`, and Node.js, Python, Go or an
existing OpenTelemetry service use `server_capture`. Save the telemetry key as
`ANECTICO_INGEST_KEY` and capture key as `ANECTICO_CAPTURE_API_KEY` in server development
configuration, or pass the distinct SDK options on clients. Agent credentials remain separate.

Each key has its own create/retry operation. A failure in one does not recreate the other.
Copy each secret before leaving. Retrying a committed request can recover metadata but never
its secret; revoke that key before creating a replacement. If you leave during an uncertain
request, check **API keys** under **Agents and keys** (`/agents`) for the key before starting another.

For individual keys, open **Agents and keys** in the Console, select **Generate API Key** and choose
a purpose and project. Intake permissions are fixed. Optionally supply an existing source ID for the same
project and purpose; leaving it empty resolves the stable default source. The key list shows
the immutable purpose/source binding. Editing can disable intake by removing the one allowed
permission, but cannot change that binding. Management / agent keys use separate permissions.

## Create a key through the CLI or API

Authenticate the CLI with a management credential that has `api_key:write`, `projects:read`
(to resolve the selected project), and the scope being granted. The CLI selects the only allowed
scope for a native purpose. It also needs `mcp:write`, because every CLI write uses the
server preview/confirmation flow. The first call shows complete current/proposed metadata,
source binding and checked scopes without creating a credential or default source. Approve
that preview, then repeat identical arguments with the printed `--idempotency-key` and
`--confirm-token`. Only the confirmed response can contain the secret:


```bash
anectico --project PROJECT_UUID apikey create --name website --purpose browser_capture
anectico --project PROJECT_UUID apikey create --name mobile-app --purpose mobile_capture
anectico --project PROJECT_UUID apikey create --name backend-events --purpose server_capture
anectico --project PROJECT_UUID apikey create --name collector --purpose native_ingest
```

Add `--source-id SOURCE_UUID` to use an existing source of the same purpose. The response shows
its source and purpose; `anectico apikey list -o json` returns `ingress_purpose` and
`ingress_source_id`. Save each plaintext secret immediately: it is returned only at creation.
Do not publish a server or management key in browser code or distributed mobile apps.

The equivalent `POST /api/v1/account/api-keys` body is:

```json
{
  "name": "backend-events",
  "project_id": "11111111-1111-4111-8111-111111111111",
  "purpose": "server_capture",
  "scopes": ["analytics:write"]
}
```

The example UUID must be replaced with your project. Optional `source_id` must identify an active
source in that project with the same purpose. Native keys forbid `org_wide`, extra scopes and
management scope profiles. Missing `purpose` means a management key; it cannot enter intake.
Expiry, containment and revocation apply to native keys as described in
[authentication](/docs/reference/authentication). Scopes can be removed to disable a key but
cannot be widened beyond its original write family.

## Retry creation safely

Supply a stable `idempotency_key` in the API body, or `--idempotency-key` in the CLI:

```bash
anectico --project PROJECT_UUID apikey create --name backend-events \
  --purpose server_capture --idempotency-key backend-events-2026-09
```

Keep the same authenticated actor, project, name, purpose, source selection, scopes and expiry
on retries. Use an explicit project; changing which default project is selected changes the
request. Retry keys contain 1–128 printable ASCII characters without spaces. The CLI requires
an explicit name and a fixed `--expires-at` or no expiry; `--expires-in` would change on every call.
A new expiry must be in the future and at most 365 days away. An identical retry may refer to
an already expired key.

Only the first committed response contains `plaintext_key`. Identical retries return current
key metadata, `replayed: true` and an empty secret. The creation record survives cache expiry,
key expiry and revocation; it never reactivates or creates a replacement key. Reusing that retry
key with different creation arguments returns a conflict. Fresh permissions still apply.
The CLI and MCP require a stable retry key; CLI generates and prints one if omitted on preparation.
Direct account API creation without a key creates a new credential each time.

If the first response is lost, retry to recover the key ID, revoke that key, then create a new
operation with a different retry key. A secret cannot be retrieved later. Keep your management
credential available for recovery; the new intake key cannot manage credentials.

## Manage keys through MCP

Use `list_read_actions` / `execute_read_action` for `list_api_keys` and `get_api_key`. They return metadata
only and require `mcp:read` plus `api_key:read`. The list has an optional limit (1–500, default 50)
and an opaque cursor; pass its `next_cursor` unchanged.

Discover `create_intake_key` and `revoke_api_key` with `list_write_actions`, then preview and
confirm through `execute_internal_action`. Both require `mcp:write`, `api_key:write` and a stable
`idempotency_key`. Creation also requires the purpose's single write scope and an explicit
`project_id`. Revocation also requires `api_key:read`; confirmation binds the displayed current
key metadata. A project-bound caller cannot operate outside that project.

Creation arguments are `project_id`, `name`, `purpose`, `idempotency_key`, and optional
`source_id` / fixed RFC3339 `expires_at`. Only the initial committed MCP **response text**
contains the secret. Structured receipts, stored replay results and audit records contain no
secret. MCP's short-lived response cache can replay the original creation metadata; use
`get_api_key` for current state. The durable creation record still prevents duplicate creation
after that cache expires. Use the recovery procedure above if the first response is lost.

MCP native creation supports the four purposes above and optional `agent_asset_id`, matching
CLI `--agent`. Management and experiment-delivery application keys use `create_api_key` through
the same preview/confirmation door. Management creation names an exact project or
`org_wide: true` and the complete caller-held scope set. Application creation needs
`experiments:launch` and grants only `experiments:deliver`. Revocation is permanent; repeating it
does not restore authority. These are credential controls, not source catalog editing.

## Send and verify

Use `X-Anectico-API-Key`, `X-API-Key` or `Authorization: ApiKey` over HTTP. For OTLP gRPC, use
`x-anectico-api-key` metadata. Do not send a bearer token alongside the key.

Configure collectors with the `native_ingest` key. Standalone product clients such as JavaScript
`AnalyticsClient` and Go `NewAnalytics` take the capture key in their own `apiKey`/`APIKey`
option. Keep it separate from the key used by an OTLP exporter and from management credentials
used by replay, flag reads, CLI or MCP. Reading a flag and recording its exposure now use different
credentials.

The JavaScript core client (`init`, `initNode`, `initBrowser`, `AnecticoClient`) now accepts
`apiKey` / `ANECTICO_API_KEY` for native OTLP and `captureApiKey` /
`ANECTICO_CAPTURE_API_KEY` for identity/group capture. Use `server_capture` in Node and
`browser_capture` in the browser. Keys must differ. Without `captureApiKey`, a non-empty core
identify/group call throws before changing identity or sending; denied product consent remains a
no-op. Standalone `AnalyticsClient` continues to take its capture key in `apiKey`.

Go uses `WithAPIKey` / `Config.APIKey` for native OTLP and `WithCaptureAPIKey` /
`Config.CaptureAPIKey` for server identity/group capture. Python uses `api_key` and
`capture_api_key` (also Django `ANECTICO["CAPTURE_API_KEY"]`). Both SDKs read
`ANECTICO_API_KEY` and `ANECTICO_CAPTURE_API_KEY`. Capture configuration is optional for
telemetry-only clients, but keys must differ when supplied. Missing/malformed/shared capture
credentials fail before identity changes or capture requests: Go returns an error; Python raises
`ValueError`, including for `sync_person`. Standalone Python `AnalyticsClient` and Go
`NewAnalytics` continue to take a capture key in their own API-key field. Go has no JWT intake mode.

iOS, Android, React Native and Flutter accept `apiKey` for native OTLP and a separate optional
`captureApiKey` for `mobile_capture`. Without a capture key, product events and identify/group
calls are no-ops before identity changes or queue admission; configuration logs a warning.
Malformed or shared capture keys reject configuration. Queued product events remain pending
without a capture key; flush reports false until drained or explicitly purged by consent withdrawal.
Adding a key does not renew withdrawn consent. Explicit logout/reset behavior is unchanged.

Older repository test fixtures and demos are still being migrated to this separation. A client
that reuses one credential for capture and OTLP cannot authorize both paths.

Each request rechecks the current credential and source without a successful-auth cache. Revoked
keys or sources and wrong-purpose credentials are refused before parsing or publishing telemetry.
Missing or malformed source authority and unavailable dependencies fail closed with a retryable
unavailable response; concurrency limits remain retryable. An unavailable check never falls back
to a management token or a development organization header.

The server stamps every admitted event with the key's source and its purpose-derived origin
(`browser_product`, `server_product`, `mobile_product` or `otlp`). Event properties or OTLP
attributes cannot grant a different origin or source. A browser or mobile purpose is a credential
declaration, not proof of the device that sent the request. Revocation stops new admissions; a
record accepted before revocation may still finish processing. Project or organization deletion
stops that completion.

A successful capture response acknowledges queued delivery. To confirm stored evidence, follow
[verify your setup](/docs/start/verify-setup), or ask your agent to run the instrumentation doctor
(`instrumentation_doctor` over MCP, or `anectico doctor instrumentation`). See
[Diagnose missing data and verify a customer story](/docs/instrument/diagnostics). Source-bound admission does
not by itself establish event authenticity or browser consent. Identical `message_id` retries are
counted once. See
[product capture admission](/docs/reference/limits#product-capture-admission).

Read one key with `anectico apikey get KEY_ID` or MCP `get_api_key`. Both return complete nonsecret metadata and mark customer-written names as untrusted.

The server key preview shows both the complete credential metadata and the complete intake source
it will bind. If a default source is new, its `before` state is null; confirming creates that source
and the key. An existing source appears unchanged in both states. Preparation creates neither.
