Skip to main content
The Moneda REST API endpoints are organized by domain. All endpoints use JSON request/response bodies and require an OAuth 2.0 Bearer token unless noted otherwise.
Write endpoints that involve money — like initiating payments — always require your confirmation in the Moneda app before anything happens.

Base URL

All endpoints are prefixed with /v1. Include your Bearer token in the Authorization header:

Response shape: ?view=full vs ?view=lite

Every GET /v1/* endpoint accepts an optional view query parameter that selects the response shape. This lets LLM agents and terminals consume a compact version of each response without dropping existing integrations.
On a typical support-chatbot turn mix, ?view=lite reduces response payloads by ~20–80% per endpoint (see the per-endpoint notes below). The difference is most dramatic on /v1/wallet, /v1/virtual-accounts, and the balance snapshot-fallback path, where the full shape carries UI coaching text that an LLM reasoning about “how much do I have” doesn’t need.
lite is a strict subset of fields with two renames:
  • balances[].accountNamebalances[].type (on /v1/balances, for semantic clarity)
  • points_activity[].createdAtpoints_activity[].date
Empty arrays, empty objects, null, and "" are also dropped from the response body on every lite call — 0, false, and "0" are preserved (balance of zero is a real answer). This is a minor shape change on lite only; the full shape is unchanged. Example:
Routes whose response is already minimal (/v1/health, /v1/exchange-rates, /v1/points/balance, /v1/settings, knowledge search) ignore view because there’s nothing to trim.

Health

GET /v1/health

Server health check. No authentication required. Response

Balances & Finances

GET /v1/balances

Returns your account balances. Scope: read:balances Query parameters Response (full)
Response (?view=lite) Drops source, snapshotTimestamp, and notice. On the snapshot-fallback path only, collapses all 4 metadata fields to one optional stale field. Renames accountNametype per row.

GET /v1/lifetime-savings

Returns your lifetime accumulated savings from using Moneda — the total fees Moneda covered on your behalf (sponsored network and transfer fees) plus the discount your plan gives you versus the Standard plan, in your display currency. Scope: read:balances Response

GET /v1/transactions

Returns your transaction history with powerful filtering. Scope: read:transactions Query parameters Response (full)
signer names the passkey that approved the payment: label is the nickname (falling back to the provider name) and kind is primary or backup. It is absent on rows that no user signed — anything created before signer provenance was recorded, and every server-initiated movement such as an incoming deposit, a scheduled transfer, an on-ramp, or a card settlement. Absent means “no user signature on file”, not “an unrecognised key”.
Exchange transactions include additional fields:
Counterparty types Response (?view=lite) Drops typeGroup, type, category, note, reference, exchanged*, received*, and counterparty raw address/iban/account/routing (already masked, still unused by LLM agents). Keeps hash, direction, amount, currency, status, date, and the trimmed counterparty. Per row ≈40–50 % smaller.

GET /v1/transactions/{hash}

Single-resource lookup by transaction hash. Scope: read:transactions Path parameters Query parameters Returns a single transaction (same shape as the list items above). 404 if not found or not owned by this user.

GET /v1/spending/by-type-group

Spending grouped by transaction type group (formerly /v1/spending/summary). Scope: read:spending Query parameters Response

GET /v1/spending/by-category

Spending grouped by user-assigned category (Groceries, Restaurants, Transport, etc.). Transactions without a category land in an UNCATEGORIZED bucket. Scope: read:spending Query parameters

GET /v1/spending/by-contact

Total amount sent to a single recipient (Moneda user, external bank, or external wallet) over a window. Scope: read:spending Query parameters

GET /v1/spending/by-merchant

Spending grouped by recipient. Each Moneda user gets their own bucket; off-ramps share one bucket per type (bank-withdrawal, external-wallet, coinbase, wallet-connect). Scope: read:spending Query parameters

GET /v1/spending/comparison

Compare spending totals across two arbitrary windows (e.g. this month vs. last month) with absolute and percentage delta. Scope: read:spending Query parameters

GET /v1/spending/daily

Daily spending totals for a stablecoin over a rolling window or absolute UTC range. Scope: read:spending Query parameters

GET /v1/spending/transactions

List the underlying transactions for a spending window (or a category/merchant bucket). Used to drill into a spending breakdown. Scope: read:spending Query parameters

GET /v1/exchange-rates

Current USD/EUR exchange rate. Scope: read:rates Query parameters Response

GET /v1/apy-rates

Current earnings APY rates. Scope: read:earnings Query parameters Response

GET /v1/dinari/capabilities

What the user may currently do with tokenized equities (dShares). Scope: read:account Response
blockedReasons is ordered — render the first entry as the next thing the user should fix. It is empty if and only if canTrade is true.

GET /v1/dinari/us/requirements

What a US-jurisdiction user still owes before their brokerage KYC can be submitted. Scope: read:account Returns field names and counts only — never the user’s answers. A non-US user gets isUsCustomer: false and an empty, already-complete requirement set. Response
There is no write counterpart to this endpoint, by design. The US profile answers and the ten regulatory disclosures are completed by the user in the Moneda app: a consent recorded without the user having personally seen the verbatim document is not a consent, and the row carries an IP and timestamp that a broker-dealer treats as a signature. Use this endpoint to explain what is outstanding and send the user to onboardingPath.

GET /v1/dinari/market-hours

The US equity session clock. No scope required: this is the public exchange calendar and carries no account data. Response
windows are ISO instants for the trading day named by cycleEtDate. On a weekend or holiday that is the next trading day, so there is always a concrete cycle to show. Render the instants in the reader’s own timezone.

GET /v1/dinari/orders/free-trade-allowance

Commission-free stock trades left in the current window, without quoting an order. Scope: read:account Response
The window steps from the customer’s own subscription anniversary, clamped on short months. It does not reset on the 1st, so quote resetsAt rather than a day of the month. degraded: true means the count could not be read and the values are a fail-open default. Say so; do not present them as fact. upgrade is populated only once the allowance is spent, and names the cheapest higher plan whose allowance would still have covered this window.

GET /v1/earnings/time-series

Daily time series of earnings and balance for a Morpho or YO vault account, used for charting yield over time. Scope: read:earnings Query parameters Response

Account Info

GET /v1/wallet

Your smart wallet address on Base. Scope: read:account Query parameters Response (full)
Response (?view=lite) — ~76% smaller

GET /v1/virtual-accounts

IBAN and ACH virtual account details for receiving transfers. Scope: read:virtual_accounts Query parameters Response (full)
Status can be active, pending, or not_registered. Response (?view=lite) — ~78% smaller

GET /v1/virtual-accounts/limits

Remaining spending limit per direction on a rolling 52-week window, plus enhanced-due-diligence (EDD) status. Scope: read:virtual_accounts Crossing a limit does not reject a transfer — the banking partner pauses it and gives the user 24 hours to supply a Source-of-Wealth document before it is returned. Check this before proposing a large deposit or withdrawal. Response
threshold: null means the customer has no enforced cap — not that the value is unknown. Limits are per direction and are not a combined budget: a customer may use their full limit on deposits and on withdrawals in the same window. Thresholds are personalised per customer (derived from declared income), so never hard-code one — always read it here.

GET /v1/virtual-accounts/settlement-estimate

The date a US bank (ACH) transfer settles. Scope: read:virtual_accounts Accounts for the 3:00 PM ET same-day cutoff, weekends, and US federal bank holidays — a transfer sent the afternoon before Thanksgiving does not arrive the next day. USD/ACH only; no equivalent published cutoff exists for EUR/SEPA. Query parameters Response
settlesOn is a calendar date in the bank’s timezone (ET). delayedByHoliday is true when a bank holiday, not merely a weekend, pushed the date out.

GET /v1/accounts

Your saved external wallets and bank accounts. Scope: read:external_accounts Query parameters Response (full)

GET /v1/contacts

Your saved contacts (Moneda users, external wallets, and bank accounts). Scope: read:contacts Query parameters Response

GET /v1/settings

Your app preferences. Scope: read:settings Response

GET /v1/passkeys

Your authentication passkeys. Scope: read:passkeys Query Response (target=WALLET) kind tells a backup apart from the key you signed up with, and onchainStatus says whether it can actually sign yet: a pending backup is registered but its add-owner transaction has not confirmed.
Response (target=LOGIN) The credential id is masked and the public key is never returned.
This endpoint reports which keys exist, not which one signs next. The preferred signing key is chosen on the user’s own device and is never stored server-side, so no API can report it. Don’t infer it from kind or ordering.

PATCH /v1/passkeys/{id}/label

Rename one of your passkeys. Scope: write:profile Cosmetic only — it cannot add, remove, or weaken a credential. Use the id from GET /v1/passkeys. target is WALLET for a smart-account passkey or LOGIN for an email login passkey. A null or empty label clears the nickname so the derived provider name is shown again. Labels are capped at 40 characters and are not required to be unique. Body
Response
Returns 404 when the id does not belong to you.

GET /v1/recovery-contacts

Your recovery guardians. Scope: read:recovery Response

Recovery emails (ZK email recovery)

The recovery-emails endpoints surface the email addresses set up for ZK email-based account recovery. Mutations (invite, accept, recover) require a passkey-signed UserOp from the mobile app and are not exposed over REST. All four endpoints share the read:recovery scope and accept an optional accountId query parameter — when omitted, the call defaults to the authenticated user’s main LOCAL account.

GET /v1/recovery-emails

List recovery emails set up for the account. Query parameters Response
When no recovery emails are configured, the response includes a friendly message field instead of an empty array.

GET /v1/recovery-emails/{id}

Single recovery email lookup by id. 404 if not found or not owned by this user (unified to prevent existence leakage). Path parameters Query parameters Returns the same shape as the list rows, plus the on-chain accountAddress the email protects.

GET /v1/recovery-config

Recovery configuration for an account: accepted recovery emails, total weight, and the threshold required to approve a recovery. Query parameters Response
The threshold is a fixed value (currently 100) that must match the on-chain Universal Email Recovery Module configuration. Recovery is approvable when totalWeight >= threshold.

GET /v1/recovery-status

Most recent recovery request snapshot for the account. Reads from the database only — agents tolerate the slight lag while the cron job syncs on-chain state. The mobile app additionally polls on-chain. Query parameters Response (no recovery in flight)
Response (recovery in flight)
hasActiveRecovery is true only for the active states (initiated, threshold_met, executing); terminal states (cancelled, completed, expired, failed) flip it back to false while the latest recovery details are still returned for context. Recovery email addresses are anonymized to first-letter***@domain in the active payload.

Notifications

GET /v1/notifications

Paginated in-app notifications inbox, newest first. Scope: read:notifications Query parameters Response
nextCursor is omitted on the last page. metadata shape varies by type — agents should treat it as opaque unless they recognise the type. Mark-as-read / dismiss / clear-all are tied to the active mobile push session and stay in tRPC; the REST surface is read-only.

GET /v1/notifications/unread-count

Unread notification count. Scope: read:notifications Response

Sub-accounts (vaults)

Sub-accounts (a.k.a. vaults) are nested Safe smart accounts owned by your main account. The endpoints below are read-only — vault create / top-up / withdraw / freeze / close all require a passkey-signed UserOp from the mobile app. All endpoints require the read:sub_accounts scope.

GET /v1/sub-accounts

List the user’s sub-accounts. Query parameters Response

GET /v1/sub-accounts/{id}

Single sub-account detail by id. Path parameters Returns the full sub-account shape including parent + child Safe addresses, members, deployment index, and close timestamp.

GET /v1/sub-accounts/{id}/balance

On-chain balances for a sub-account’s child Safe. Path parameters Query parameters Balances are returned as raw bigint strings (6 decimals).

GET /v1/wealth

Aggregate wealth across the main account + every live vault. Scope: read:balances + read:sub_accounts. Query parameters Returns per-leg breakdown (wallet + Morpho + YO for the main account; wallet-only for vaults) plus per-currency totals. USDC and EURC are cross-converted using the on-chain Aerodrome rate.

Sessions

API key authentication produces sessions tracked in the database for audit trails. Bearer-token (OAuth) requests don’t create sessions.

POST /v1/sessions

Create a session record (typically called by clients that hold long-lived API keys). Scope: depends on the calling channel. Request body
Response

DELETE /v1/sessions/{sessionId}

End a session. Path parameters Returns 204 No Content on success.

GET /v1/sessions/active

List active login sessions and connected agents (third-party services). Scope: read:sessions Response

DELETE /v1/sessions/active/{sessionId}

Terminate a specific active session. Scope: write:sessions Path parameters Response

POST /v1/sessions/revoke-all

Terminate all other active sessions, keeping the caller’s current session. Scope: write:sessions Response

Points & Referrals

GET /v1/points/balance

Your points balance. Scope: read:points Response

GET /v1/points/activity

Points earning history. Scope: read:points Query parameters Response

GET /v1/points/redemption-config

Config for redeeming points into AI credits: the points-per-$1 rate, the minimum redemption, the monthly cap, your current balance, and how much monthly allowance is left. Scope: read:points Response

POST /v1/points/redeem

Redeem Moneda Points for prepaid AI usage credits. Spends points and grants the matching AI budget atomically, enforcing the minimum, sufficient-balance, and monthly-cap checks. Scope: write:usage_credits Body Response

GET /v1/referrals/code

Your referral link and stats. Scope: read:referrals
The referral code is derived from your account (<username>-<id-prefix>). Set a username first — the code is unavailable until you do.
Response

GET /v1/referrals/referees

Friends you’ve referred. Scope: read:referrals Response

Knowledge Base

These endpoints are public — no authentication required.

GET /v1/knowledge/search

Search the FAQ knowledge base. Query parameters Response

GET /v1/knowledge/categories/

Browse a FAQ category by slug. Path parameters Response

GET /v1/knowledge/items/

Get a specific FAQ item. Path parameters Response

Users

GET /v1/users/search

Search Moneda users by username or display name. Scope: read:users Query parameters Response

Write Endpoints

PATCH /v1/profile

Update your display name. Scope: write:profile Request body
Response

PATCH /v1/transactions/{hash}/category

Assign a spending category to a transaction. Scope: write:transactions Path parameters Request body
Valid categories: TOP_UP, INVESTMENT, FRIENDS_FAMILY, INTEREST_EARNINGS, SUBSCRIPTIONS, SERVICES, GROCERIES, SHOPPING, RESTAURANTS, TRANSPORT, TRAVEL, UTILITIES, CASH, SALARY, FUEL, EV_CHARGING, GAMBLING, CHARITY_DONATIONS, TAXES, INSURANCE, CARD, HEALTHCARE, GENERAL Response

PATCH /v1/transactions/{hash}/note

Add or update a note on a transaction. Scope: write:transactions Path parameters Request body
Response

POST /v1/transactions/batch/categories

Categorize up to 25 transactions at once. Scope: write:transactions Request body
Response

POST /v1/transactions/batch/notes

Add notes to up to 25 transactions at once. Scope: write:transactions Request body
Response

POST /v1/accounts/wallets

Save an external crypto wallet. Scope: write:external_accounts Request body
Set confirmed: false first to check for warnings (e.g. if the address belongs to another Moneda user). Then resend with confirmed: true to proceed. Response
Or if confirmation is needed:

POST /v1/accounts/banks

Save a bank account (EU IBAN or US ACH). Scope: write:external_accounts Request body (EU)
Request body (US)
Response

POST /v1/payments

Initiate a payment. Requires approval in the Moneda app. Scope: write:payments
Payments are not executed immediately. After calling this endpoint, the user must approve the payment in the Moneda mobile app within 5 minutes.
Request body
Payment types Response

GET /v1/payments//status

Check the status of a payment request. Scope: write:payments Path parameters Response
Payment statuses: PENDING_APPROVAL, APPROVED, COMPLETED, FAILED, EXPIRED, REJECTED

POST /v1/x402-payments

Pay an x402 (HTTP 402 Payment Required) invoice to a wallet address. Requires approval in the Moneda app, then settles on-chain to the resource’s payTo address. Scope: write:payments
Like other payments, x402 invoices are not executed immediately — the user must approve in the Moneda mobile app within 5 minutes. Poll GET /v1/payments/{id}/status for the result, then retry the original HTTP request once it is COMPLETED.
Request body
Response

Payment Routing

These endpoints help an agent or client choose a payment corridor and quote the cost before calling POST /v1/payments. They are provider-agnostic — don’t branch on which underlying processor handles each rail.

GET /v1/payment-destinations

List the country, currency, and rail combinations Moneda can pay out to. Each entry includes the required fields you’ll need to collect from the user before calling POST /v1/payments. Scope: read:external_accounts Query parameters

GET /v1/payment-quote

Preview the cost, FX rate, and final amount for moving money between two currencies. Provide exactly one of amountIn (what the user sends) or amountOut (what they want to receive). Scope: read:rates Query parameters

Receipts

Receipts are user-uploaded files (PDFs, images, structured invoices) the platform OCRs, extracts to structured fields, and optionally auto-matches to a transaction. Bills are the invoice-shaped sibling (AP queue) covered separately below.

GET /v1/receipts

List the user’s transaction receipts, newest first, with extracted summary fields and lifecycle status. Scope: read:receipts Query parameters: transactionHash (optional), needsReview (true/false, optional), status (optional), limit (default 20, max 100), cursor (optional).

GET /v1/receipts/{receiptId}

Get one receipt with full extracted fields and a presigned S3 download URL valid for ~15 minutes. Scope: read:receipts

GET /v1/receipts/{receiptId}/line-items

List parsed line items on a receipt — description, quantity, unit price, total, tax rate, category. Scope: read:receipts

POST /v1/receipts/{receiptId}/line-items

Add a line item to a receipt. Used after manual extraction review. Scope: write:receipts

PATCH /v1/receipts/line-items/{lineItemId}

Update one line item on a receipt (description, quantity, price, tax rate, category). Scope: write:receipts

DELETE /v1/receipts/line-items/{lineItemId}

Remove one line item from a receipt. Scope: write:receipts

GET /v1/receipts/line-items/search

Search across every line item the user has uploaded (case-insensitive substring on description). Returns matching items + aggregate spend grouped by currency. Optional category and date-range filters. Scope: read:receipts

POST /v1/receipts

Create a receipt row (metadata only; for direct upload use the upload-url + finalize flow). Scope: write:receipts

DELETE /v1/receipts/{receiptId}

Delete a receipt and its line items. Scope: write:receipts

POST /v1/receipts/upload-url

Step 1 of 2 — get a presigned S3 PUT URL to stage a local file. Returns uploadUrl, uploadKey, and constraints (size cap, content-type whitelist). The file bytes go directly to S3. Scope: write:receipts

POST /v1/receipts/finalize

Step 2 of 2 — after PUTting the file to S3, finalize the receipt to run OCR + LLM extraction, attempt auto-match to a transaction, and persist line items. Pass the uploadKey from /v1/receipts/upload-url. Scope: write:receipts

POST /v1/receipts/import-url

Import a receipt directly from an https:// URL (S3, Drive shared link, public file host). The server fetches behind an SSRF guard — rejects private IPs and non-https. Single call. Scope: write:receipts

POST /v1/receipts/{receiptId}/attach

Link a receipt to a transaction (used after manual review when auto-match didn’t run or was wrong). Scope: write:receipts

POST /v1/receipts/{receiptId}/detach

Unlink a receipt from its transaction. Scope: write:receipts

POST /v1/receipts/{receiptId}/confirm-suggestion

Accept the auto-match suggestion the OCR pipeline proposed. Scope: write:receipts

POST /v1/receipts/{receiptId}/reject-suggestion

Reject the auto-match suggestion (the receipt stays unlinked). Scope: write:receipts

Bills (AP queue)

Bills are the invoice-shaped sibling of receipts. They surface as an accounts-payable queue ordered due-soon-first, and carry merchant + payment routing detail extracted by OCR.

GET /v1/bills

List the user’s bills (AP queue), ordered dueDate ASC NULLS LAST, id DESC. Cursor-paginated; defaults to status=PENDING. Scope: read:receipts

GET /v1/bills/{billId}

Get one bill with full extracted detail — merchant (name, tax id, address), total, currency, due date, invoice number, vendor IBAN/BIC/routing/account, payment status, presigned download URL. Scope: read:receipts

Bewirtungsbelege (German hospitality receipts)

Bewirtungsbelege are an overlay on existing transactions that capture German tax-deductible business-meal detail (occasion, attendees, deductible/non-deductible split). They reference a transaction; the receipt file itself stays in the Receipts table.

GET /v1/bewirtungsbelege

List the user’s Bewirtungsbelege. Each row carries the underlying transaction, date, location, occasion, total/tip/subtotal split, deductible/non-deductible amounts, and signing state. Scope: read:receipts

GET /v1/bewirtungsbelege/{bewirtungsbelegId}

Get one Bewirtungsbeleg with full detail including attendees. Scope: read:receipts

POST /v1/bewirtungsbelege

Create a Bewirtungsbeleg overlay for an existing transaction. Scope: write:receipts

DELETE /v1/bewirtungsbelege/{bewirtungsbelegId}

Delete a Bewirtungsbeleg overlay. The underlying transaction and receipt file are untouched. Scope: write:receipts

Eigenbelege (German self-issued substitute receipts)

Eigenbelege are used when the original receipt for a transaction was lost or never issued. They capture the recipient, amount, and reason for the substitute receipt.

GET /v1/eigenbelege

List the user’s Eigenbelege. Each row carries the linked transaction, recipient name, amount, reason, and signing state. Scope: read:receipts

GET /v1/eigenbelege/{eigenbelegId}

Get one Eigenbeleg with full detail. Scope: read:receipts

POST /v1/eigenbelege

Create an Eigenbeleg overlay for an existing transaction. Scope: write:receipts

DELETE /v1/eigenbelege/{eigenbelegId}

Delete an Eigenbeleg overlay. The underlying transaction is untouched. Scope: write:receipts

Cards

Card endpoints return PCI-redacted metadata only — last four digits, expiry, design, status. Full PAN / CVV / PIN are never available via the API. Freeze and unfreeze run on-chain via the PausableCardGuard and are fully reversible.

GET /v1/cards

List the user’s cards (active, suspended, expired, terminated) with PCI-redacted metadata. Scope: read:cards

GET /v1/cards/{cardId}

Get detailed metadata for one card (status, type, program, currency, expiry, design). Returns NOT_FOUND if the card does not exist or does not belong to the caller. Scope: read:cards

POST /v1/cards/{cardId}/freeze

Freeze a card on-chain via the PausableCardGuard. Settlement is blocked at the Roles Module level. Idempotent at the storage level (re-freezing succeeds, but a fresh on-chain tx is submitted on every call). Returns CONFLICT if the card’s on-chain proxy hasn’t been deployed yet. Scope: write:cards

POST /v1/cards/{cardId}/unfreeze

Unfreeze a card on-chain via the PausableCardGuard. Restores settlement. Idempotent at the storage level. Scope: write:cards

Scheduled (Recurring) Transactions

Read + lifecycle (pause/resume) endpoints for scheduled transactions. Create / activate / cancel / edit / renew are NOT exposed via REST — they need a passkey signature on a Smart Sessions install UserOp, which is mobile-app only.

GET /v1/scheduled-transactions

List the user’s scheduled (recurring) transactions. Filter by status. Scope: read:transactions

GET /v1/scheduled-transactions/{id}

Get one scheduled transaction by id. Scope: read:transactions

GET /v1/scheduled-transactions/upcoming

List the next scheduled executions across all the user’s schedules. Scope: read:transactions

GET /v1/scheduled-transactions/{id}/history

List past executions for one schedule. Scope: read:transactions

POST /v1/scheduled-transactions/{id}/pause

Pause execution of a schedule (backend-only state flip; no on-chain change). Scope: write:transactions

POST /v1/scheduled-transactions/{id}/resume

Resume execution of a paused schedule. Returns the new nextExecutionAt, which is always in the future — an execution window that elapsed while the schedule was paused is skipped, never run retroactively. Fails with 422 if the schedule’s end date has already passed. Scope: write:transactions

Agent Sessions

Read + lifecycle (pause/resume/revoke) endpoints for scoped autonomous agent sessions — a bounded mandate an agent can spend against without per-payment approval. Create / execute and the passkey-bound install are served separately. Both scopes are opt-in (not granted by default).

POST /v1/agent-sessions

Create a scoped autonomous payment session. Does NOT activate — returns a counterfactual child Safe address and an activation link the user opens in the Moneda app to install the on-chain policy with a passkey. Body: recipient, currency (USD/EUR), maxAmountPerExecution, maxAmountPerPeriod (user-facing units), periodSeconds, optional expiresAt, fundingAmount. Scope: write:agent_sessions

GET /v1/agent-sessions

List the user’s agent sessions, newest first, with status, spending policy, and remaining period cap. Optional status query filter. Scope: read:agent_sessions

POST /v1/agent-sessions/{id}/payments

Execute a payment within an ACTIVE session’s on-chain policy — no per-payment approval. Body: recipient, amount (user-facing units), currency, optional idempotencyKey for safe retries. Scope: write:agent_sessions

GET /v1/agent-sessions/{id}

Get one agent session by id, including its 5 most recent executions. Scope: read:agent_sessions

POST /v1/agent-sessions/{id}/pause

Pause an active session (backend-only flip; halts execution, reversible via /resume). Scope: write:agent_sessions

POST /v1/agent-sessions/{id}/resume

Resume a manually-paused session. Auto-paused sessions need their underlying condition cleared first. Scope: write:agent_sessions

POST /v1/agent-sessions/{id}/revoke

Revoke a session — immediately and permanently halts all execution. The on-chain uninstall is completed separately in the Moneda app. Scope: write:agent_sessions

Usage Credits

POST /v1/usage-credits/purchase

Redeem a completed top-up payment to Moneda for prepaid AI usage credits. The amount is read from the confirmed on-chain payment (never from input), converted to USD budget, and granted exactly once — re-calling with the same hash is safe (idempotent). The payment is already finished, so there is nothing more to approve. Body: transactionHash. Scope: write:usage_credits Response

PATCH /v1/usage-credits/monthly-limit

Set or clear the monthly spend limit on AI usage credits — the ceiling on how much of the prepaid balance may be consumed per calendar month (resets the 1st, UTC). Body: monthlyLimitUsd (number 0–100000, or null for unlimited). Scope: write:usage_credits Response

Subscriptions

Read + invoice-generation surface for subscription billing. The write surface (subscribe / cancel / change plan) stays in tRPC for now — it needs on-chain Smart Session ceremonies that don’t fit a synchronous REST request. Gated by the ENABLE_BALANCE_PAY_SUBSCRIPTIONS feature flag in the services layer (a disabled flag surfaces as 403).

GET /v1/subscriptions/charges

Cursor-paginated list of the authenticated user’s subscription charges (SUBSCRIPTION_CHARGE rows), newest first. Scope: read:subscriptions

POST /v1/subscriptions/charges/{chargeId}/invoice

Idempotently generate (and upload) the ZUGFeRD plan-invoice PDF for a charge and return a 1-hour presigned downloadUrl. Re-calling for the same charge reuses the existing invoice. Returns 404 if the charge doesn’t exist or isn’t owned by the caller. Scope: read:subscriptions

Platform (Management API)

The Platform Management API (/v1/platform/*) lets developers manage their organization, workspaces, members, invites, and platform API keys. These endpoints are authorized by the calling user’s organization role (owner / admin / developer / billing / compliance / member), not by MCP scopes. See the Moneda Platform for the full model.

POST /v1/platform/organizations

Create an organization. The caller is seated as its OWNER. Body: name, slug.

GET /v1/platform/organizations

List the organizations the caller is an active member of, with their role in each.

POST /v1/platform/organizations/{orgId}/workspaces

Create a workspace under an organization. Requires a key-manager role (owner / admin / developer). Body: name, slug.

GET /v1/platform/organizations/{orgId}/workspaces

List the workspaces in an organization.

POST /v1/platform/organizations/{orgId}/members

Add (or re-activate) a member by user id. Requires a member-manager role (owner / admin); only an owner may grant or modify the OWNER role. Body: userId, role.

GET /v1/platform/organizations/{orgId}/members

List the members of an organization with their roles.

POST /v1/platform/organizations/{orgId}/invites

Invite a member by email. Requires a member-manager role. Returns the raw invite token once. Body: email, role.

GET /v1/platform/organizations/{orgId}/invites

List an organization’s invites with their status (pending / accepted / expired). Never exposes the token hash.

POST /v1/platform/invites/accept

Accept an invite. Body: the raw invite token. Seats the caller as an organization member.

POST /v1/platform/workspaces/{workspaceId}/keys

Issue a workspace-scoped platform API key. Requires a key-manager role. The raw secret is returned exactly once. Body: name, scopes (money-movement scopes are not available on platform keys), optional mode. mode: "live" (default) mints an mnd_live_… key billed against org credits; mode: "test" mints an mnd_test_… sandbox key — it never decrements credits, never calls paid upstreams, and uses separate rate buckets. (Sandbox tool calls are served as deterministic fixtures; tool execution over platform keys — live and sandbox — ships with the tool API and is not yet available.) Live and sandbox keys have independent per-workspace caps. The response sandbox flag reflects the key’s mode.

GET /v1/platform/workspaces/{workspaceId}/keys

List a workspace’s platform API keys (metadata only — never the secret).

POST /v1/platform/keys/{keyId}/rotate

Rotate a platform key: mints a replacement and puts the old key on a grace window so it can be swapped with zero downtime. Requires a key-manager role.

DELETE /v1/platform/keys/{keyId}

Revoke a platform key immediately. Requires a key-manager or compliance role.

POST /v1/platform/organizations/{orgId}/limits

Create or reconfigure a spend/rate limit. Requires a member-manager role (owner / admin). Body: scope (ORG/WORKSPACE/KEY), targetId, limitType (SPEND_HARD/SPEND_SOFT/RATE_RPM/RATE_TPM/RATE_RPD), value, optional windowSeconds (rolling window; defaults to the calendar month for spend), and optional notifyThresholdPct (1–99). Upserts on (scope, targetId, limitType).

GET /v1/platform/organizations/{orgId}/limits

List every limit configured for the org (its own, plus workspace- and key-scoped limits). Any member may read.

DELETE /v1/platform/organizations/{orgId}/limits/{limitId}

Remove a limit. Requires a member-manager role.

POST /v1/platform/workspaces/{workspaceId}/webhooks

Register a webhook endpoint for a workspace. Requires a member-manager role. Body: url (https only, and must resolve to a public address — loopback/private/link-local targets are rejected at registration and re-checked at every delivery), optional description, and enabledEvents (one or more of the event catalog below). The signingSecret is returned exactly once — store it to verify signatures.

GET /v1/platform/workspaces/{workspaceId}/webhooks

List a workspace’s webhook endpoints (metadata only — never the signing secret).

PATCH /v1/platform/webhooks/{webhookId}

Update an endpoint’s url, description, enabledEvents, or status (ENABLED/DISABLED). Requires a member-manager role.

DELETE /v1/platform/webhooks/{webhookId}

Delete a webhook endpoint (cascades its pending deliveries). Requires a member-manager role. Event catalog: usage.threshold_reached, usage.limit_exceeded, usage.rate_limited, credit.low, credit.exhausted, key.created, key.revoked. Signature verification. Each delivery is a POST with headers X-Moneda-Event, X-Moneda-Delivery, X-Moneda-Timestamp, and X-Moneda-Signature. Verify by computing HMAC-SHA256(signingSecret, "v0:" + timestamp + ":" + rawBody) (hex) and comparing against X-Moneda-Signature; reject timestamps outside a few minutes to prevent replay. Delivery is at-least-once. A retry (or an overlapping dispatch of a still-in-flight delivery) can send the same event more than once. Deduplicate on X-Moneda-Delivery (unique per delivery attempt) or the event’s id, and make your handler idempotent. Return a 2xx promptly; any other status is retried with exponential backoff up to 6 attempts.

GET /v1/platform/organizations/{orgId}/audit-log

Read the organization audit log (policy-changing actions: key/limit/webhook/member changes), newest first. Restricted to owner / admin / compliance roles. Query: optional action filter, before (opaque cursor — pass a prior response’s nextBefore), limit (default 50, max 200). Response includes nextBefore for keyset pagination.

GET /v1/platform/organizations/{orgId}/usage

Read the organization’s metered request log (one row per billed call), newest first. Any member may read. Query: optional workspaceId, apiKeyId, tool filters, before (opaque cursor — pass a prior response’s nextBefore), limit (default 50, max 200). Money fields (costUsd, upstreamCostUsd) are strings; response includes nextBefore.

GET /v1/platform/pricing

The machine-readable rate card: one row per tool with unitPriceUsd, markupMultiplier, and the effective pricePerUnitUsd (unit × markup), plus unit (PER_CALL/PER_1K_TOKENS/PER_GB_DAY), category, and scope. Money fields are decimal strings. This is the global default table.

GET /v1/platform/organizations/{orgId}/contract

Read the org’s enterprise contract (committed spend, term, SLA tier, dedicated-capacity flag, legal-doc references) plus its billingMode and dataResidency. Returns null if the org has no contract. Any member may read.

PUT /v1/platform/organizations/{orgId}/contract

Create or update the enterprise contract terms. Requires a member-manager role. Body (all optional): status (DRAFT/ACTIVE/EXPIRED/TERMINATED), committedAmountUsd, termStart, termEnd, autoRenew, slaTier, dedicatedUpstreamCapacity, legalDocRefs ([{ kind, url, version?, signedAt? }] — pointers to MSA/DPA/SLA, never the documents), dataResidency. Provisioning is sales-led; in production this is a Moneda-admin action.

POST /v1/platform/organizations/{orgId}/billing-mode

Set the org’s billing mode. Requires a member-manager role. Body: billingMode (PREPAID or INVOICED). INVOICED — where usage accrues to a monthly invoice against the contract instead of decrementing prepaid credits — requires an ACTIVE contract.

Error Responses

All errors follow a consistent format:
Error codes

What’s next?

Authentication

Learn how to authenticate your API requests.

OpenAPI Spec

Explore the API interactively with Swagger UI.

Scopes

See which scopes each endpoint requires.

CLI Tool

Use these endpoints from the command line with the Moneda CLI.