Skip to content
Apier.no

Authentication

Apier's consumer auth model — API keys, tiers, per-endpoint access categories, and the sandbox isolation contract.

[Cite this as: Apier.no Docs v0.1.0 — last updated 2026-07-22]

Apier uses bearer API keys for authentication. Every key is SHA-256 hashed at rest and is never logged. Keys are issued in the dashboard and shown in full exactly once — store yours securely.

Getting an account and your first key

Creating the account always involves a person — someone opens one emailed link, once, to bring the account into being. (Once the account exists, its owner can issue keys to headless agents without a browser — see Headless key issuance below.) The flow:

  1. Sign up. POST /api/v1/account/signup with a JSON body { "email": "...", "company_name": "...", "consent": true }. All three fields are required to sign up — sending email alone is instead treated as a returning-user login. A 200 with { "success": true, "data": { "ok": true } } means the request was accepted.
  2. A human opens the magic link. The signup call issues no API key. It emails a magic link to the address you supplied; someone with access to that inbox must open it. The account, and its first key, are created on that first sign-in — not by the signup call.
  3. The first key is shown once. On that first sign-in Apier provisions a free-tier key (apr_free_…) and shows it in full exactly once. From then on you manage your keys — up to three — from your dashboard.

Before signing up you can read pricing with no key (GET /api/v1/pricing), review the terms, and exercise the documented public-sandbox endpoints against org 999999999. Issuing further keys with POST /api/v1/account/keys also needs an authenticated browser session, not a bearer token — see Key rotation below. The one headless path is the owner-issued issuance token, next.

Headless key issuance (owner-issued tokens)

An agent can obtain its own API key with no browser and no inbox — but only because a human chose to let it. The account owner mints a one-time issuance token in the dashboard and hands it to the agent out of band; the agent redeems it, once, for a read:*-scoped key. Agents cannot self-issue: there is no endpoint that turns "I am an agent" into a credential — only the owner-minted token does, and the owner can revoke it any time before redemption.

The full path from cold agent to first authenticated call:

  1. Sign up and sign in once (the human steps above) — the account must exist.

  2. Owner mints a token. In the dashboard, under Agent issuance tokens, mint a token (apr_issue_…). It is shown in full exactly once, is valid for 24 hours, is strictly single-use, and at most 3 unredeemed tokens can be outstanding at a time. Only its SHA-256 hash is stored.

  3. Owner hands the token to the agent — through whatever secret channel already exists (environment variable, secrets manager, MCP client config).

  4. Agent redeems it:

    curl -X POST https://www.apier.no/api/v1/account/issuance-tokens/redeem \
      -H "content-type: application/json" \
      -d '{"token":"apr_issue_<your_token_here>"}'

    The 201 body returns the agent's own key (plaintext_key, scopes ["read:*"]) exactly once — the same one-time contract as every other key mint. The redemption is atomic and single-use: a second redemption of the same token fails, and two concurrent redemptions issue exactly one key. An MCP-pure agent redeems the same token without leaving the protocol via the keyless redeem_issuance_token tool — same route, same semantics, same one-time contract. Minting and revoking tokens remain dashboard-only by design; there is no mint tool on any agent surface.

  5. First authenticated call with the returned key, e.g. GET /api/v1/company/999999999/summary.

Failure behaviour is deliberately uniform: an expired, already-used, revoked, or never-existed token all return the same 401 ISSUANCE_TOKEN_INVALID body — the only fix is a fresh token from the owner. One exception is disclosed: if the account already holds 3 active keys, redemption returns 409 MAX_KEYS_REACHED and the token is not consumed, so the agent can retry with the same token after the owner frees a key slot. Redemptions are rate-limited per IP, and every mint, revoke, and redemption lands in the account's audit trail (the redemption event records the issued key id and the terms version in force).

Making an authenticated request

Pass your key in the Authorization header:

GET /api/v1/company/{org}/summary HTTP/1.1
Authorization: Bearer apr_free_<your_key_here>

The MCP server and the quickstart examples set this header for you.

Key formats

One prefix scheme runs across the whole API, so you can tell at a glance what a token is and where it works:

PrefixWhat it isWhere it works
apr_free_…A free-tier key, issued by default on first sign-inEvery endpoint; Category B at the Free-tier rate limit
apr_test_…An issued test-tier keyEvery endpoint, against synthetic data
apr_live_…An issued production keyProduction /api/v1/* — real filings
apr_issue_…A one-time key-issuance token, minted by the account owner in the dashboardOnly POST /api/v1/account/issuance-tokens/redeem — it is not a bearer key and authenticates nothing else
apier_sandbox_test_<suffix>The no-signup synthetic sandbox bearerThe /api/v1/sandbox/* routes only

Issued keys (apr_…) come from your dashboard and are shown in full exactly once. The synthetic sandbox bearer needs no signup — the <suffix> (1–64 characters of [A-Za-z0-9_-], typically a crypto.randomUUID()) is a private session namespace you choose. See the quick start for the sandbox walkthrough.

Access categories

Apier endpoints fall into two categories:

CategoryAuth requiredExamples
Category ANo — open to any caller/api/v1/public/obligations, /api/v1/public/deadlines, /api/v1/health/agent-readiness
Category BYes — valid API key/api/v1/company/{org}/summary, /api/v1/company/{org}/obligations, /api/v1/actions/execute

Category A endpoints are safe to call from a browser or an unauthenticated agent to discover obligations and deadlines without exposing a key.

Keyless MCP tools

Six public tools on the MCP server are callable via tools/call without an API key — five wrap zero-auth read endpoints, and one wraps the zero-auth issuance-token redemption route:

  • get_public_obligations
  • get_public_deadlines
  • explain_compliance_error
  • get_exchange_rate
  • get_pricing
  • redeem_issuance_token — converts a one-time issuance token the account owner minted in the dashboard into the agent's own read:*-scoped key; keyless by necessity, since the caller cannot hold a key yet

Keyless tools/call requests are rate-limited per IP (100 requests / hour). Every other MCP tool — company data, acting capacity, authorization, and actions — requires a key with the matching scope and returns 401 without one. (The one non-key credential the MCP surface accepts is the synthetic apier_sandbox_test_<suffix> bearer described under the sandbox isolation contract below — it authenticates sandbox-routed tools only, never the production surface.)

"Without an API key" means no Authorization header at all. If you do send a header but the key is invalid or revoked, the call returns 401 — it is not silently downgraded to the keyless path, even for one of the keyless public tools.

The sandbox and its isolation contract

The sandbox surface (/api/v1/sandbox/...) returns deterministic synthetic data and never calls a government system. It accepts the no-signup apier_sandbox_test_<suffix> bearer (and a real key too), and the boundary between sandbox and production is enforced both ways:

  • A real key used on /api/v1/sandbox/* is accepted, but the response is still mock — a sandbox request never reaches a government upstream, so nothing is ever submitted there.
  • The synthetic bearer is rejected on production /api/v1/* routes with 401 AUTH_INVALID_KEY — it works only on the sandbox surface.

So a misrouted call fails safe in both directions: a production key cannot accidentally file through the sandbox, and a sandbox bearer cannot reach the real API. See Going live for the switch to real filings.

Rate limits

Company-data (Category B) limits scale with your tier:

TierCategory B limit
Free30 requests / min
Starter150 requests / min
Professional300 requests / min
EnterpriseUnlimited

Category A endpoints share a separate public rate limit (1 000 requests / min per IP) and are not counted against your tier quota.

Scopes

Each key carries a scopes array (default ["read:*"]). Category B endpoints declare the scope they require — for example, /api/v1/actions/execute requires read:actions. Reserved prefixes (write:, act:, delegate:) are not assignable today. The full assignable set is listed on /api/v1/capabilities.

Key rotation

You may hold up to 3 active keys at once. That overlap capacity is what makes rotation zero-downtime: a new key and the key it replaces are both valid at the same time, so there is never a window where every credential is dead.

The MCP server authenticates with the same bearer key as the REST API — it reads it from the Authorization: Bearer header — so rotating an MCP consumer's key is the same three-step flow, applied to whatever holds that header (an environment variable, a secrets manager, your agent's MCP client config):

  1. Mint the new key. POST /api/v1/account/keys returns the plaintext key exactly once in the response body — store it immediately. (The dashboard's "Create key" button does the same thing.) You now hold both the old and the new key.
  2. Cut over, then verify. Point the Authorization: Bearer header at the new key and confirm a real call succeeds — for MCP, a tools/call that returns a result; for REST, any Category B request that returns 200. Because the old key is still live, a failed cutover is recoverable: roll the header back and retry.
  3. Revoke the old key. DELETE /api/v1/account/keys/{id} (or "Revoke" in the dashboard) takes effect immediately. From that moment the old key is rejected on every surface — each MCP tools/call and each REST request made with it returns 401 AUTH_INVALID_KEY (the same unified failure shape a never-issued key returns, so a revoked key discloses nothing about its former validity). The new key keeps working uninterrupted.

Revocations and rejected calls are recorded in your account's audit trail, so a rotation — and any lingering client still presenting the retired key — is auditable after the fact.

There is no single "rotate" endpoint: rotation is deliberately the two primitives above (POST to mint, DELETE to revoke) so the overlap window — and the verification step inside it — stays under your control. If you already hold 3 active keys, revoke one before minting the replacement.

How keys are enforced

All authentication logic lives in src/middleware/auth.ts and src/lib/auth/*. On every Category B request the key is resolved, validated against the SHA-256 hash stored in Supabase, scope-checked, and rate-limited before the handler runs.

Building with an LLM? Read llms.txt for agent-oriented integration guidance.