---
title: "Agent-initiated top-up request (human approval required)"
method: POST
path: "/api/v1/billing/topup-requests"
tags: ["Account"]
---

# Agent-initiated top-up request (human approval required)

`POST /api/v1/billing/topup-requests`

## Request

- Base URL: `https://www.apier.no`
- URL: `https://www.apier.no/api/v1/billing/topup-requests`
- Auth: HTTP bearer

Lets a Bearer-authenticated agent REQUEST a prepaid-credit top-up of a specific whole-øre amount for its own API key — the machine-readable recovery path a 402 INSUFFICIENT_CREDITS body advertises in its `topup_request` field. This endpoint is deliberately Stripe-free: it never creates a checkout session, never touches a card, and never charges anyone, in any mode. It only records a PENDING request (24-hour expiry) that a HUMAN reviews on the billing dashboard (`approval_url`); only that human approval drives the existing Stripe checkout, and the human still completes payment on Stripe's hosted page. The amount is bounded twice: a 1000-øre floor (NOK 10 — below that, card fees eat the top-up) and an adjustable server-side ceiling read live from billing settings (1000 NOK at launch; over-ceiling requests get a 400 naming both the requested amount and the current ceiling, plus both remedies — lower the amount, or hand over to a human who can top up without any ceiling). Every valid, non-revoked key may call this (no scope requirement): requesting is not a data read, and a key that just hit 402 must be able to ask for funding regardless of how narrowly its scopes are cut. Both the consumer and the funded key are taken exclusively from the authenticated key — the body carries only the amount, so cross-key or cross-tenant requests are structurally impossible. Abuse-bounded by a fail-closed 5/hour per-key rate limit and a cap of 3 outstanding pending requests per consumer. Supports the Idempotency-Key header (a replay returns the stored 201 without creating a second request).

## Request body

- object
  - `amount_ore` integer, required: Requested top-up amount in WHOLE øre (integer øre only, no floats; 100 øre = NOK 1). Must be at least 5000 øre (NOK 50, the same floor as a direct dashboard top-up) and at most the current agent top-up ceiling — an adjustable server-side setting (100000 øre = 1000 NOK at launch) enforced live at BOTH request creation and human approval, so a lowered ceiling takes effect on already-pending requests. The 402 body's `topup_request.ceiling_ore` and the TOPUP_CEILING_EXCEEDED error's `ceiling_ore` both carry the current value.

## Response `201`

The pending top-up request. Nothing has been charged and no checkout exists yet — a human must approve the request at `approval_url` before any payment can start; unresolved requests expire at `expires_at` (24 hours after creation).

- object
  - `success` true, required
  - `data` object, required
    - `id` string, uuid, required: The request's id — the join key the audit trail records as `request_id`.
    - `amount_ore` integer, required: The validated requested amount in whole øre, echoed back verbatim.
    - `status` 'pending', required: Always `pending` at creation. The request becomes `approved` or `declined` only through a human decision on the dashboard, or is treated as expired once `expires_at` passes.
    - `created_at` string, date-time, required: Creation instant (RFC 3339, UTC).
    - `expires_at` string, date-time, required: Expiry instant, 24 hours after creation. A request not approved or declined by then can no longer be resolved — the agent may simply request again.
    - `approval_url` string, uri, required: The HUMAN handover: the billing-dashboard page where the operator reviews and approves or declines this request. Byte-identical to the `top_up_url` the 402 body advertises. Surface it to your operator verbatim.
  - `_meta` object, required

## Other responses

- `400`: Validation failure. Three distinct machine-readable classes: VALIDATION_FAILED (malformed body — `amount_ore` missing, non-integer, non-positive, or extra keys present), TOPUP_BELOW_MINIMUM (amount under the 1000-øre floor; body carries `requested_ore` + `minimum_ore`), and TOPUP_CEILING_EXCEEDED (amount over the current adjustable ceiling; body carries `requested_ore` + `ceiling_ore` and fix_steps offering BOTH remedies — lower the amount, or hand over to a human who can top up without a ceiling at the dashboard). All use the standard bilingual error envelope.
- `401`: Missing, malformed, revoked, or unknown API key — the standard AUTH_* envelope. There is no keyless path to this endpoint.
- `409`: MAX_PENDING_TOPUP_REQUESTS — this consumer already has 3 pending (unexpired) requests awaiting human review. Wait for one to be approved, declined, or to expire (24h), or ask the operator to review them on the dashboard.
- `429`: RATE_LIMITED — more than 5 requests in the current hour for this key (fail-closed: a rate-limit infrastructure failure also refuses). Retry-After header carries the wait in seconds.
- `503`: TOPUP_CEILING_UNAVAILABLE — the adjustable ceiling could not be read, so the request was refused without being created (a money-adjacent bound is never guessed). Transient; retry shortly.

---

[API](https://skmtc.dev/apier/apis/apier-no-api.md) · [All operations](https://skmtc.dev/apier/apis/apier-no-api/llms.txt) · [OpenAPI document](https://skmtc.dev/apier/apis/apier-no-api/revisions/9ec19bc87311?raw)
