System tokens — mint, rotate, retention, revoke

A system token is the credential your code uses to ingest into ActivityLog. Every POST carries Authorization: Bearer al_… against the receiver.

There are two token shapes:

Prefix Purpose Used at
al_… Native ingest + OTLP + Langfuse POST /messages, POST /otlp/v1/logs, POST /otlp/v1/traces, POST /api/public/ingestion
alw_… Webhook receiver POST /webhooks/{alw_…}

This page covers al_…. For webhook tokens see 53-ingest-webhooks.md.

Mint a token

  1. Sidebar → Systems → click the system (or New System if you don't have one).
  2. On the system detail page click New Token.
  3. Pick a retention (see below).
  4. Click Generate.
  5. The token is shown once. Copy it now to your secret store.

Token mint dialog with retention dropdown

The portal will never show you the token value again. We store only a one-way hash — there is no recovery path. If you lose it, revoke and mint a new one.

Retention — per token, not per tenant

Each token has its own retention window. This is the platform's key differentiator: a single system can have two tokens, one with 5-year retention (for compliance-shaped events) and one with 90-day retention (for high-volume analytics).

Allowed values:

Days Use case
7 Free-tier default; ephemeral testing
30 Short-lived dev environments
90 Pro-tier default; normal app activity
180 Half-year audit window
365 1y — generic ingest, no specific compliance need
730 2y
1095 3y
1825 5y — typical security-audit window; recommended for M365 sources
-1 Never expire (Enterprise only)

The portal dropdown shows only values your tier allows. Free is capped at 7. Pro is capped at 90 unless you've negotiated an override. Enterprise can pick any value including -1.

Changing retention after mint

Systems → → → Edit retention. The new value applies to future messages — already-stored messages keep the retention they were ingested under.

Token retention edit panel

Rotation

There's no "rotate" button — rotation is mint-new + revoke-old:

  1. Mint a new token under the same system.
  2. Deploy the new token to your app(s).
  3. Verify ingest is flowing under the new token (Messages view shows the system + token id on each message).
  4. Systems → → Tokens → → Revoke.

The revoked token rejects all subsequent requests with 401 Unauthorized.

Tokens table with Revoke action

Tokens have no expiry by default. Rotate every 90 days as a habit even if there's no leak suspected; rotation cost is one redeploy + one revoke click.

Revocation

A revoked token cannot ingest. The data it ingested is not affected — only future requests fail.

To revoke: Systems → → Tokens → Revoke. There's no undo. If you revoke by accident, mint a new one.

What a token can and can't do

A system token is write-only and scoped to one system in one tenant. With a token you can:

  • POST to /messages (single + batch) for the bound system
  • POST to /otlp/v1/logs and /otlp/v1/traces for the bound system
  • POST to /api/public/ingestion (Langfuse) for the bound system
  • GET /systems/me to confirm which system the token belongs to

You cannot:

  • Read any messages back
  • Touch any other system
  • Touch any other tenant's data
  • Mint, rotate, or revoke other tokens (use the portal or a user JWT)

If a token is leaked, the blast radius is one system's ingest — not the whole tenant.

Best practices

  • One token per source environment. Production app gets its own token; staging gets its own; CI gets its own. When something goes wrong you can revoke just that environment.
  • Long-retention token for audit-shaped events, short for analytics. Most M365 integrations use 5-year tokens. App logs typically use 90-day.
  • Never embed in client-side code. Tokens are bearer credentials — anyone who sees the JS source can ingest as you. For browser/mobile clients, proxy through your backend.
  • Watch the headers. Successful ingest returns X-RateLimit-Remaining — log it. When you see it falling below 10% of capacity, you're about to hit the burst cap.
  • Free-tier sampling. Above the burst cap on Free, the API returns X-AL-Sampled: true and drops 1-in-N requests. If you see that header in logs, you're outgrowing Free.

Tier caps recap

Capability Free Pro Enterprise
Tokens per system 2 50 unlimited
Retention range 1–7 days 7–90 days per contract
Steady ingest rate 100 msg/s 5,000 msg/s per contract
Burst (10 s) 1,000 msg/s 50,000 msg/s per contract
Sampling above burst? yes no no

Numbers from ../Pricing.md.

What's next

Goal Doc
POST your first message 00-quickstart.md
Native ingest deep-dive (batch, dedup, duration, metadata) 50-ingest-native.md
OpenTelemetry collector setup 51-ingest-otlp.md
Webhook token (for Event Grid / ADO) 53-ingest-webhooks.md

Troubleshooting

401 Unauthorized — "invalid token". The token is wrong, revoked, or scoped to a different environment than the host you're hitting. Check the system detail page in the portal to confirm the token still shows as Active.

429 Too Many Requests with Retry-After: N. You've hit the burst cap. On Free, you'll also see X-AL-Sampled: true on subsequent requests — some are dropped silently. Either upgrade to Pro, or batch your POSTs.

413 Payload Too Large. Your message body is over the tier inline cap (16 KB on Free, 256 KB on Pro). Either shrink the body (most over-the-cap payloads embed something that should be a separate Mode-2 ingest), or upgrade.

My token works on staging but fails on production. Staging and production are separate environments with separate databases. Tokens minted on activitylog-api.betawebserver.com will not work on api.activitylog.com.