Agent payments — discover prices, survive 402, top up, retry
The full prepaid-credit payment loop for autonomous agents — price discovery at /api/v1/pricing, the machine-readable 402 recovery contract, agent-initiated top-up requests with human approval, and balance visibility on every metered response.
[Cite this as: Apier.no Docs v0.1.0 — last updated 2026-07-31]
Apier's company-data reads are metered against a prepaid credit balance held per API key. This guide is written for machine consumption first: exact field names, exact status codes, and complete example bodies, so an agent (or the developer wiring one) can implement the whole payment loop without guesswork. Every amount on this API is a whole-øre integer (100 øre = NOK 1) — no floats, no fractional øre, ever.
One honesty note up front: while credit enforcement runs dark (the launch
default), metered calls are not charged and a 402 is impossible — those
responses carry the X-Credit-Check: shadow header instead. Everything below
describes the live-enforcement contract, which is also what the
enforcement.live field on /api/v1/pricing reports. That pricing response
is served Cache-Control: no-store, so the flag is never stale out of a
shared cache — but treat it as best-effort discovery: the authoritative
charging signal is always the metered response itself (X-Credit-Check: shadow = not charged; X-Credits-* headers or a 402 body = enforcement
live), never a previously fetched price list.
Two ways to pay — and they are independent
When billing goes live there are two mechanisms, and the most important thing to encode is that they do not interact.
- A monthly subscription tier buys throughput and support. It sets your enforced rate limits — requests per minute, a stricter per-minute limit on company-data endpoints, and a per-organisation daily cap — plus your support level. It does not bundle an allowance of metered calls. There is no monthly included-call quota and no per-call overage.
- Prepaid credits are a balance you top up, from which each metered company-data read draws its cost (50 øre for a company-data read). A credit balance does not raise any rate limit.
So there is no ordering question and nothing is "spent first": metered reads always draw from the credit balance, and the subscription always governs the ceilings. The consequences for an agent are concrete:
- A zero credit balance means metered reads will 402, regardless of which subscription tier the account is on. Do not treat a paid subscription as a reason to keep calling.
- Hitting a rate limit is a
429withRetry-After, never a charge. Back off; topping up will not help. - Read your ceilings rather than guessing them.
GET /api/v1/account/usagereturns alimitsobject (per_minute_category_a,per_minute_category_b,per_org_daily);nullon a numeric field means unlimited, never zero.
1. Discover prices before paying — GET /api/v1/pricing
GET https://www.apier.no/api/v1/pricing is public and keyless: an agent can
price a workflow before it holds an API key or a balance. The response is
derived from the canonical pricing configuration the 402 meter enforces —
there is no second price table anywhere in the system. A previously fetched
price can still become stale after a pricing change, so the per-call metered
response (not a stored price list) remains the authoritative charging signal.
{
"success": true,
"data": {
"schema_version": "1.0.0",
"currency": "NOK",
"credit_unit": "Whole øre (1 øre = NOK 0.01). Every cost, balance, and top-up amount on this API is a whole-øre integer — no floats, no fractional øre.",
"metered_endpoints": [
{
"endpoint": "/api/v1/company/{org}/context",
"method": "GET",
"category": "rulebook_read",
"cost_ore": 50,
"mcp_tool": "get_company_context"
}
],
"enforcement": {
"live": true,
"authority_note": "Best-effort discovery signal. The authoritative charging signal is always the metered response itself: X-Credit-Check: shadow means the call was not charged; X-Credits-* headers or a 402 INSUFFICIENT_CREDITS body mean enforcement is live. Never infer charging behaviour from a previously fetched price list alone.",
"insufficient_balance_status": 402,
"insufficient_balance_error_code": "INSUFFICIENT_CREDITS",
"recovery_contract_fields": ["retryable", "fix_hint", "docs_url", "top_up_url", "balance_ore", "cost_ore", "topup_request"]
},
"balance_visibility": {
"rest_headers": ["X-Credits-Balance-Ore", "X-Credits-Cost-Ore", "X-Credits-Warning", "X-Credits-Warning-Threshold-Ore", "X-Credits-Topup-Url"],
"mcp_metadata_field": "metadata.credits",
"balance_endpoint": "/api/v1/account/credits/balance"
},
"low_balance_warning": {
"factor": 5,
"rule": "A warning is emitted when the post-debit balance is strictly below factor × the call's cost_ore."
},
"top_up": {
"min_ore": 5000,
"max_ore": 1000000,
"top_up_url": "https://www.apier.no/dashboard/billing",
"bounds_scope": "dashboard_checkout",
"paths": {
"dashboard_checkout": {
"description": "A HUMAN completes a card payment on the billing dashboard. These are the bounds the checkout session validates.",
"min_ore": 5000,
"max_ore": 1000000,
"top_up_url": "https://www.apier.no/dashboard/billing"
},
"agent_request": {
"description": "An AGENT requests funding without leaving the API; a human then approves it on the billing dashboard and pays. The agent surface never touches a card. Lower floor than the dashboard path, and the ceiling is an operator-adjustable server-side setting rather than a code constant.",
"endpoint": "/api/v1/billing/topup-requests",
"method": "POST",
"amount_field": "amount_ore",
"min_ore": 1000,
"max_ore": null,
"max_ore_source": "billing_settings.agent_topup_ceiling_ore",
"max_ore_note": "Operator-adjustable; read live at both request and approval. The current value is carried on the 402 INSUFFICIENT_CREDITS body as topup_request.ceiling_ore."
}
}
},
"subscription_tiers": {
"pricing_json_url": "https://www.apier.no/pricing.json",
"pricing_page_url": "https://www.apier.no/pricing"
},
"how_to_pay_guide": "https://www.apier.no/docs/guides/agent-payments"
}
}Read top_up carefully: the two funding paths have different bounds.
top_up.min_ore / top_up.max_ore are the dashboard checkout bounds —
that is what bounds_scope tells you — and top_up.paths states each path
explicitly. The agent top-up request path (§4b) has a lower floor and its
ceiling is null here on purpose: that ceiling is an operator-adjustable
server-side setting, and a keyless cacheable surface must not advertise a
value that may already have changed. The live ceiling reaches you on the 402
body as topup_request.ceiling_ore.
subscription_tiers points back at the other billing mechanism. This
endpoint prices metered per-call reads only; tier prices and the rate limits
a tier buys live at /pricing.json.
The list above is truncated to one metered endpoint for readability — the live
response enumerates every metered endpoint. MCP tools inherit metering through
their REST forward (one debit point), so the mcp_tool name next to each
endpoint costs exactly the same as the endpoint itself, and every metered MCP
tool's description also states its own cost.
2. Authenticate
Metered endpoints require an API key in the Authorization header:
curl -H "Authorization: Bearer apier_test_<your_key_here>" \
https://www.apier.no/api/v1/company/999999999/contextA brand-new key has a balance of 0 øre — authoritatively zero, not unknown. Check any key's own balance at any time (this read is deliberately scope-exempt and never debits):
curl -H "Authorization: Bearer apier_test_<your_key_here>" \
https://www.apier.no/api/v1/account/credits/balance3. Interpret the 402 — the machine-readable recovery contract
With an insufficient balance, a metered call returns HTTP 402 with
error_code: "INSUFFICIENT_CREDITS". Nothing was charged: the balance is
checked-and-debited atomically before the handler runs, so an insufficient
balance leaves both the balance and the ledger untouched, deterministically.
A complete example 402 body:
{
"success": false,
"error_code": "INSUFFICIENT_CREDITS",
"explanation": {
"summary": "Forhåndsbetalt kredittsaldo er for lav for dette kallet.",
"summary_en": "Prepaid credit balance is too low for this call.",
"why": "Dette endepunktet koster 50 øre per kall, men saldoen på API-nøkkelen er 0 øre. Ingen belastning er gjort.",
"why_en": "This endpoint costs 50 øre per call, but the API key's balance is 0 øre. Nothing was charged.",
"fix_steps": [
"Fyll på kredittsaldoen fra faktureringssiden (se top_up_url).",
"Prøv forespørselen på nytt etter påfyll."
],
"fix_steps_en": [
"Top up the credit balance from the billing page (see top_up_url).",
"Retry the request after topping up."
]
},
"retryable": false,
"fix_hint": "Prepaid balance too low — top up (see top_up_url), then retry the request.",
"fix_hint_en": "Prepaid balance too low — top up (see top_up_url), then retry the request.",
"docs_url": "https://www.apier.no/docs/guides/agent-payments",
"top_up_url": "https://www.apier.no/dashboard/billing",
"balance_ore": 0,
"cost_ore": 50,
"topup_request": {
"endpoint": "/api/v1/billing/topup-requests",
"method": "POST",
"amount_field": "amount_ore",
"ceiling_ore": 100000
},
"_meta": {
"rulebook_version": "1.0.0",
"data_freshness": "2026-07-18T12:00:00.000Z",
"last_verified": "2026-07-18T12:00:00.000Z",
"source": "apier.no",
"schema_version": "1.4.0",
"response_timestamp": "2026-07-18T12:00:00.000Z",
"response_hash": "sha256:…"
}
}How an agent should read it:
retryable: false— an identical retry with an unchanged balance fails identically. Do not loop.balance_oreandcost_ore— compute the shortfall (cost_ore - balance_ore) and how much runway a given top-up buys.top_up_url— the human handover. Topping up is a payment action; a human completes it on the billing dashboard. Surface this URL to your operator verbatim.topup_request— the agent-initiated alternative: instead of only handing over a URL, the agent can itself request a top-up of a specific amount at the embedded endpoint (see the next section).ceiling_oreis the current server-side cap on such requests, shown here at its launch value of 100000 øre (NOK 1,000, operator-adjustable). If it ever readsnull, the ceiling could not be read at that instant; the request endpoint enforces the authoritative ceiling either way.- After the top-up lands, retry the identical request. Reads are side-effect free; the retry is safe.
The related failure 503 CREDIT_CHECK_UNAVAILABLE (credit infrastructure
unreachable) is the opposite: retryable: true — wait a few seconds and retry
the identical call. Its body states the actual money outcome in
explanation.why_en.
4. Top up — the dashboard checkout path
Top-ups are made by a human on the billing dashboard at the exact
top_up_url the 402 body carries (byte-identical to the one
/api/v1/pricing and /api/v1/account/credits/balance advertise).
Bounds for THIS path (dashboard checkout, human pays by card):
min_ore: 5000 (NOK 50) to max_ore: 1000000 (NOK 10,000) per
transaction. These are the top_up.min_ore / top_up.max_ore values on
/api/v1/pricing, labelled there by
top_up.bounds_scope: "dashboard_checkout". They are not the bounds on
the agent-request path in §4b, which has a lower floor and a live,
operator-adjustable ceiling — do not validate an amount_ore against the
wrong set.
Credits are granted exactly once per completed payment — replayed payment events are deduplicated server-side.
4b. Or request a top-up as the agent — the agent-request path
An agent can also request a top-up itself, without leaving the API. Be honest with your operator about what this is: the request creates a pending review item — a human approves it, and a human pays. The agent surface never touches the payment. In detail:
curl -X POST \
-H "Authorization: Bearer apier_test_<your_key_here>" \
-H "Content-Type: application/json" \
-d '{"amount_ore": 10000}' \
https://www.apier.no/api/v1/billing/topup-requests{
"success": true,
"data": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"amount_ore": 10000,
"status": "pending",
"created_at": "2026-07-23T09:00:00.000Z",
"expires_at": "2026-07-24T09:00:00.000Z",
"approval_url": "https://www.apier.no/dashboard/billing"
}
}The rules an agent can rely on:
- Any valid key may request. No scope is required — a key that just hit 402 can always ask for funding. The request funds the calling key only; the amount is the only body field.
- Floor: 1000 øre (NOK 10). Below that, card fees eat the top-up —
you get
400 TOPUP_BELOW_MINIMUMwithrequested_ore+minimum_ore. - Ceiling: adjustable, 100000 øre (NOK 1,000) at launch. The ceiling
is a server-side setting read live at BOTH request creation and human
approval — lowering it takes effect on requests that are already
pending. Over the ceiling you get
400 TOPUP_CEILING_EXCEEDEDwithrequested_ore+ceiling_oreand both remedies: lower the amount, or hand over to your operator, who can top up without any ceiling at the dashboard. - A human decides. The request appears on the operator's billing
dashboard (
approval_url). Approval opens the same Stripe-hosted checkout as a direct top-up, which the human completes (or abandons) there; decline ends the request with no payment surface ever touched. Nothing is charged by the request itself, in any mode. - Expiry: 24 hours. An unresolved request expires; simply request again. At most 3 pending requests per account and 5 requests per hour per key.
- Idempotency: send an
Idempotency-Keyheader to make retries safe — a replay returns the stored response instead of creating a second request.
After the human approves and completes payment, credits land on the key's
balance exactly as for a direct top-up (deduplicated, granted once) — poll
/api/v1/account/credits/balance or just retry the original metered call.
5. Retry and read your balance on every success
After topping up, the identical request succeeds. Every successful metered
response reports the remaining balance in response headers (the JSON body is
never mutated post-hoc — its provenance response_hash must stay
re-verifiable):
HTTP/1.1 200 OK
X-Credits-Balance-Ore: 950
X-Credits-Cost-Ore: 50
X-Correlation-ID: 550e8400-e29b-41d4-a716-446655440000When the post-debit balance drops strictly below 5× the call's cost, a low-balance warning trio rides along — the agent's cue to request a top-up before the next 402:
HTTP/1.1 200 OK
X-Credits-Balance-Ore: 200
X-Credits-Cost-Ore: 50
X-Credits-Warning: low_balance
X-Credits-Warning-Threshold-Ore: 250
X-Credits-Topup-Url: https://www.apier.no/dashboard/billing6. The same contract over MCP
MCP tools forward to the REST endpoints, so the money semantics are identical — one debit point, one price. The envelope surfaces:
- Success:
metadata.creditson the tool result —{ "balance_ore": 200, "cost_ore": 50, "balance_warning": { "threshold_ore": 250, "remaining_ore": 200, "top_up_url": "https://www.apier.no/dashboard/billing" } }(balance_warningpresent only below the threshold). - Insufficient balance:
metadata.error_code: "INSUFFICIENT_CREDITS"withmetadata.retryable: false,metadata.remediation, and the same recovery fields —metadata.top_up_url,metadata.balance_ore,metadata.cost_ore.
Charging rules an agent can rely on
- A 402 never charges (checked-and-debited atomically before execution).
- A successful call (2xx) charges exactly its advertised
cost_ore. - Outcomes where no billable work was delivered — any 5xx, 403 (scope rejection), 404 (unknown company), 304 (revalidation hit) — are automatically refunded; the ledger nets to zero for those calls.
- Same input, same price: pricing is configuration, not negotiation (Rule 9 determinism extends to the meter).
When a subscription lapses
A failed renewal does not cut an agent off at the moment the card declines, and it does not cut it off silently either. This section describes what an agent actually observes, and what it can and cannot fix on its own.
A failed renewal buys a grace window, not an instant cutoff. When a
renewal charge fails, Stripe moves the subscription to past_due. The
paid tier is kept for 7 days measured from current_period_end —
a card blip must not instantly drop a paying customer to free limits. The
boundary is exclusive: at exactly current_period_end + 7 days the paid
access ends. Stripe's own dunning retries usually resolve the state
inside that window.
The downgrade is not instantaneous when the window closes. Grace expiry is a clock event, not a Stripe event — no webhook fires at the moment it lapses. A daily reconciliation job compares every subscription against Stripe and applies the correction, so expect the downgrade to land within about a day of the window closing rather than the second it does. Design for the transition being observable, not for a precise timestamp.
Cancellation is immediate. A cancelled or expired subscription is never in grace: the consumer moves to the free tier as soon as Apier processes the cancellation event.
API keys are never revoked for billing reasons. This is the part
worth building on. A downgrade changes the ceiling, not the
credential — no key is revoked, disabled, or rotated when a
subscription lapses. Calls that fit inside the free-tier limits keep
working with the same Authorization header.
What changes is the rate limit, and the symptom is a 429. On
downgrade the per-minute Category A and Category B limits and the
per-organisation daily limit all drop to their free-tier values. An agent
that was comfortably inside its paid ceiling will start collecting
429 RATE_LIMIT_EXCEEDED responses carrying a Retry-After header.
Treat a sudden onset of 429s on unchanged traffic as a possible billing
signal, not only as a traffic-shaping problem.
Read your own ceilings instead of inferring them.
GET /api/v1/account/usage returns a limits object alongside the usage
counts:
{
"tier": "starter",
"per_minute_category_a": 300,
"per_minute_category_b": 150,
"per_org_daily": 5000,
"window_seconds": 60,
"subscription_status": "past_due"
}null on any numeric field means unlimited (the enterprise tier), never
zero calls. A null limits object means the tier could not be resolved
— treat it as unknown and back off conservatively; it is deliberately not
defaulted to the free-tier numbers. Note that subscription_status is
diagnostic only: tier is what the limiter enforces, and a past_due
status does not by itself mean the tier has already dropped — during
the grace window the status reads past_due while tier is still the
paid one.
The fix is a human handover. Restoring a lapsed subscription means
updating a payment method, which an agent must never attempt. The billing
portal at https://www.apier.no/dashboard/billing is where a human does
it; the account owner is also emailed when a downgrade is applied. This is
the same handover boundary as a top-up above a ceiling — surface it, do
not work around it.
Prepaid credits are a separate ledger. A subscription lapse does not consume, void, or refund a prepaid credit balance. The two mechanisms are independent: the subscription sets rate-limit ceilings, credits pay for metered calls.
Idempotency and safe retries
Make write requests retry-safe with the Idempotency-Key header — at-most-once execution, a 24-hour replay window, and the four reservation outcomes an agent must handle.
Billing — subscriptions, prepaid credits, invoices
How Apier billing works — the two independent payment mechanisms (a subscription tier that sets your rate limits, and prepaid credits that pay for metered company reads), invoices and receipts via Stripe, why no VAT (MVA) is charged, and how to change or cancel.