Getting started/docs/authentication

Authentication

Every endpoint on the Archivist is server-to-server. We use HTTP Bearer tokens — pass your key in the Authorization header on every request and you’re in. No cookies, no sessions, no CSRF surface to worry about.

A first request — curl

The fastest sanity check. Replace $ARCHIVIST_KEY with the key we issued you:

GET /bills···
curl https://api.archivist.dev/bills \
  -H "Authorization: Bearer $ARCHIVIST_KEY" \
  -H "Accept: application/json"

A 200 response with a populated items array means the key is good. A 401 means the header was missing or the key is wrong; a 403 means the key’s tier doesn’t have access to this endpoint.

From JavaScript

The fetch pattern is identical: set Authorization and call the endpoint. Use the built-in fetch — no SDK is required.

node-fetch.js···
// Node 20+ (built-in fetch)
const res = await fetch('https://api.archivist.dev/bills', {
  headers: {
    Authorization: `Bearer ${process.env.ARCHIVIST_KEY}`,
    Accept: 'application/json',
  },
});
if (!res.ok) throw new Error(`Archivist ${res.status}`);
const { items, nextCursor } = await res.json();

In a browser, the key should never reach the client. Pass the user’s session through your own backend and proxy the request.

browser-via-proxy.js···
// Browser-side fetch — load the key through your backend, never expose it.
const res = await fetch('/api/proxy/bills', {
  headers: { Authorization: `Bearer ${userToken}` },
});
const data = await res.json();

Key placement — what to do (and what not to)

Do

Store the key in a backend-only secret manager or environment variable.

Rotate keys via the console; we accept up to two live keys per org.

Keep keys per-environment — a separate key for staging and production.

Don’t

Never ship the key to the client bundle, a NEXT_PUBLIC_* env var, or a public repo.

Don’t hardcode the key in source — read it from env at runtime.

Don’t reuse the same key across organizations; scopes don’t overlap.

Errors & limits

Errors are JSON. Every error response carries a stable shape so clients can match on error without parsing prose:

error envelope···
{
  "error": "Rate limit exceeded"
}
Status reference
StatusWhenWhat to do
401

Missing or invalid key

The Authorization header was absent, malformed, or the key no longer exists.

Send `Authorization: Bearer <key>`; regenerate the key in the console if it was rotated out.
403

Scope or tier

The key’s tier is not entitled to this endpoint family.

Check the key is for the tier that owns this surface; contact us to upgrade.
429

Rate limit

You have exceeded the per-minute quota on your tier.

Honor the `Retry-After` header and consider upgrading; the paid beta tier is ~100 req/min.
Webhooks, not polls

Prefer the diff webhook over polling. Webhook deliveries are HMAC-SHA256 signed with X-Archivist-Signature and are replayable by cursor — a missed delivery costs nothing.