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:
- Sign up.
POST /api/v1/account/signupwith a JSON body{ "email": "...", "company_name": "...", "consent": true }. All three fields are required to sign up — sendingemailalone is instead treated as a returning-user login. A200with{ "success": true, "data": { "ok": true } }means the request was accepted. - 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.
- 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:
-
Sign up and sign in once (the human steps above) — the account must exist.
-
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. -
Owner hands the token to the agent — through whatever secret channel already exists (environment variable, secrets manager, MCP client config).
-
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
201body 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 keylessredeem_issuance_tokentool — 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. -
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:
| Prefix | What it is | Where it works |
|---|---|---|
apr_free_… | A free-tier key, issued by default on first sign-in | Every endpoint; Category B at the Free-tier rate limit |
apr_test_… | An issued test-tier key | Every endpoint, against synthetic data |
apr_live_… | An issued production key | Production /api/v1/* — real filings |
apr_issue_… | A one-time key-issuance token, minted by the account owner in the dashboard | Only 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 bearer | The /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:
| Category | Auth required | Examples |
|---|---|---|
| Category A | No — open to any caller | /api/v1/public/obligations, /api/v1/public/deadlines, /api/v1/health/agent-readiness |
| Category B | Yes — 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_obligationsget_public_deadlinesexplain_compliance_errorget_exchange_rateget_pricingredeem_issuance_token— converts a one-time issuance token the account owner minted in the dashboard into the agent's ownread:*-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 with401 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:
| Tier | Category B limit |
|---|---|
| Free | 30 requests / min |
| Starter | 150 requests / min |
| Professional | 300 requests / min |
| Enterprise | Unlimited |
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):
- Mint the new key.
POST /api/v1/account/keysreturns 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. - Cut over, then verify. Point the
Authorization: Bearerheader at the new key and confirm a real call succeeds — for MCP, atools/callthat returns a result; for REST, any Category B request that returns200. Because the old key is still live, a failed cutover is recoverable: roll the header back and retry. - 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 MCPtools/calland each REST request made with it returns401 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.