{"openapi":"3.1.0","info":{"title":"Moneda API","version":"1.0.0","description":"REST API for Moneda — manage balances, transactions, contacts, payments, and more.\n\n**Versioning.** The major version is in the path (`/v1`). Changes within a version are backward-compatible: new endpoints, new optional parameters and new response fields. A breaking change ships as a new major path (`/v2`) while `/v1` keeps running. A deprecated endpoint or version is signalled on its responses with the `Deprecation` header (RFC 9745), the `Sunset` header (RFC 8594) and a `Link` header pointing to the migration guide, and is marked `deprecated: true` in this document.\n\n**Rate limits.** Every response carries `RateLimit-Policy` and `RateLimit` (IETF draft-ietf-httpapi-ratelimit-headers), plus `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset` and the legacy `X-RateLimit-*` fields. A 429 also carries `Retry-After` in seconds."},"servers":[{"url":"https://rest.moneda.com","description":"Production"},{"url":"https://sandbox.rest.moneda.com","description":"Sandbox: mock responses generated from this document, no account and no funds. Get a key from POST /v1/sandbox/keys."}],"externalDocs":{"description":"Versioning, deprecation and rate-limit conventions","url":"https://docs.moneda.com/api/overview#versioning-and-deprecation"},"security":[{"Bearer":[]}],"components":{"securitySchemes":{"Bearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Bearer token obtained via the Moneda OAuth flow."},"ApiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key generated via the Moneda app."},"OAuth2":{"type":"oauth2","description":"OAuth 2.1 authorization code flow with PKCE. The access token is sent as a Bearer token; each operation lists the scopes it requires.","flows":{"authorizationCode":{"authorizationUrl":"https://mcp.moneda.com/api/auth/authorize","tokenUrl":"https://mcp.moneda.com/api/auth/token","refreshUrl":"https://mcp.moneda.com/api/auth/token","scopes":{"read:account":"Read access to account","read:agent_sessions":"Read access to agent sessions","read:api_keys":"Read access to api keys","read:balances":"Read access to balances","read:contacts":"Read access to contacts","read:earnings":"Read access to earnings","read:external_accounts":"Read access to external accounts","read:notifications":"Read access to notifications","read:passkeys":"Read access to passkeys","read:platform":"Read access to platform","read:points":"Read access to points","read:rates":"Read access to rates","read:receipts":"Read access to receipts","read:recovery":"Read access to recovery","read:referrals":"Read access to referrals","read:scheduled_tx":"Read access to scheduled tx","read:sessions":"Read access to sessions","read:settings":"Read access to settings","read:spending":"Read access to spending","read:sub_accounts":"Read access to sub accounts","read:subscriptions":"Read access to subscriptions","read:tax_statements":"Read access to tax statements","read:transactions":"Read access to transactions","read:users":"Read access to users","read:virtual_accounts":"Read access to virtual accounts","write:agent_sessions":"Write access to agent sessions","write:api_keys":"Write access to api keys","write:external_accounts":"Write access to external accounts","write:notifications":"Write access to notifications","write:payments":"Write access to payments","write:platform":"Write access to platform","write:profile":"Write access to profile","write:receipts":"Write access to receipts","write:scheduled_tx":"Write access to scheduled tx","write:sessions":"Write access to sessions","write:sub_accounts":"Write access to sub accounts","write:tax_statements":"Write access to tax statements","write:trades":"Write access to trades","write:transactions":"Write access to transactions","write:usage_credits":"Write access to usage credits"}}}}},"headers":{"RateLimit-Policy":{"description":"Quota policy as a structured field (draft-ietf-httpapi-ratelimit-headers): `q` is the request quota, `w` the window in seconds.","schema":{"type":"string","example":"\"default\";q=60;w=60"}},"RateLimit":{"description":"Current state of the policy as a structured field: `r` is the requests remaining, `t` the seconds until the window resets.","schema":{"type":"string","example":"\"default\";r=59;t=42"}},"RateLimit-Limit":{"description":"Request quota for the window.","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer","example":59}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer","example":42}},"X-RateLimit-Limit":{"description":"Legacy. Same value as `RateLimit-Limit`.","schema":{"type":"integer","example":60}},"X-RateLimit-Remaining":{"description":"Legacy. Same value as `RateLimit-Remaining`.","schema":{"type":"integer","example":59}},"X-RateLimit-Reset":{"description":"Legacy. Unix timestamp (seconds) at which the current window resets.","schema":{"type":"integer","example":1791321241}},"Retry-After":{"description":"Seconds to wait before retrying. Sent on 429 responses.","schema":{"type":"integer","example":42}}},"responses":{"RateLimited":{"description":"Too many requests from this IP address. Retry after the number of seconds in `Retry-After` (RATE_LIMIT_EXCEEDED)","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"parameters":{"Idempotency-Key":{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional, up to 255 characters (a UUID is ideal). Retrying with the same key and the same body within 24 hours returns the stored response with `Idempotent-Replayed: true` instead of acting again. The same key with a different body is a 422 (IDEMPOTENCY_KEY_REUSED); a retry while the first request is still running is a 409 (IDEMPOTENCY_KEY_IN_PROGRESS). Keys are scoped to the authenticated user. 5xx responses are not stored, so they can be retried with the same key.","schema":{"type":"string","maxLength":255}}},"schemas":{"HealthResponse":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"timestamp":{"type":"string"},"uptime":{"type":"number"},"version":{"type":"string"},"commit":{"type":"string"},"services":{"type":"object","properties":{"database":{"type":"string","enum":["ok","error"]}},"required":["database"]}},"required":["status","timestamp","uptime","version","commit","services"]},"SandboxKey":{"type":"object","properties":{"key":{"type":"string","description":"A sandbox key. Send it as X-API-Key to the sandbox host. It is rejected everywhere else.","example":"mnd_test_mgk3x1a0_9f2c4e6a8b0d1f3e_5d41402abc4b2a76b9719d911017c592"},"expiresAt":{"type":"string","example":"2026-01-02T00:00:00.000Z"},"sandbox":{"type":"boolean","enum":[true]}},"required":["key","expiresAt","sandbox"]},"ErrorResponse":{"type":"object","properties":{"code":{"type":"string","example":"UNAUTHORIZED"},"message":{"type":"string","example":"Invalid or missing Bearer token"},"retryable":{"type":"boolean","example":false},"details":{"type":"object","additionalProperties":{}},"requestId":{"type":"string","description":"Correlation id for this request. Quote it to support."},"retryAfterMs":{"type":"number","description":"How long to wait before retrying, when rate limited."}},"required":["code","message","retryable"]},"BalanceResponse":{"type":"object","properties":{"source":{"type":"string","enum":["live","snapshot"]},"balances":{"type":"array","items":{"type":"object","properties":{"accountName":{"type":"string"},"currency":{"type":"string"},"type":{"type":"string"},"balance":{"type":"string"}},"required":["accountName","currency","type","balance"]}},"notice":{"type":"string"},"snapshotTimestamp":{"type":"string"},"snapshotAge":{"type":"string"}},"required":["source","balances"]},"BalanceResponseLite":{"type":"object","properties":{"balances":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"type":{"type":"string","description":"The account name (`accountName` in the full shape)"},"balance":{"type":"string"}},"required":["currency","type","balance"]}},"stale":{"type":"string","description":"Age of the snapshot. Present only when the live read failed and a snapshot was returned"}},"required":["balances"]},"TransactionListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"transactionHash":{"type":"string"},"typeGroup":{"type":"string"},"type":{"type":"string"},"category":{"type":"string"},"note":{"type":"string"},"direction":{"type":"string","enum":["outgoing","incoming","exchange"]},"amount":{"type":"string"},"currency":{"type":"string"},"status":{"type":"string"},"reference":{"type":"string"},"counterparty":{"type":"object","properties":{"type":{"type":"string","enum":["self","user","provider","bank","wallet","unknown"]},"name":{"type":"string"},"isExternalUser":{"type":"boolean","description":"`user` counterparties only: false for a Moneda account, true for an external contact"},"bank":{"type":"string","description":"Bank counterparties only: the institution name"},"username":{"type":"string"},"address":{"type":"string"},"iban":{"type":"string"},"account":{"type":"string"},"routing":{"type":"string"}},"required":["type"]},"date":{"type":"string"},"exchanged":{"type":"string"},"exchangedCurrency":{"type":"string"},"received":{"type":"string"},"receivedCurrency":{"type":"string"},"asset":{"type":"object","properties":{"symbol":{"type":"string"}},"required":["symbol"],"description":"The instrument a trade was in — the crypto asset key or the dShare ticker. Present only on CRYPTO_* and DSHARE_* rows."},"trade":{"type":"object","properties":{"venue":{"type":"string","enum":["crypto","stock"]},"status":{"type":"string"},"soldAssetAmount":{"type":"string"},"minReceivedAmount":{"type":"string"},"receivedAmount":{"type":"string"},"quotedAssetAmount":{"type":"string"},"filledAssetAmount":{"type":"string"},"quotedPrice":{"type":"string"},"slippageBps":{"type":"number"},"feeAmount":{"type":"string"},"sourceFeeAmount":{"type":"string"},"monedaFeeAmount":{"type":"string"},"monedaFeeCurrency":{"type":"string"},"orderType":{"type":"string"},"orderStatus":{"type":"string"},"requestedQuantity":{"type":"string"},"limitPrice":{"type":"string"},"filledQuantity":{"type":"string"},"avgFillPrice":{"type":"string"},"submittedAt":{"type":"string"},"filledAt":{"type":"string"},"failedAt":{"type":"string"},"failureReason":{"type":"string"}},"description":"Fills, fees and any failure of a trade. Present only on trade rows, alongside `asset`."},"card":{"type":"object","properties":{"cardId":{"type":"string","format":"uuid"},"last4":{"type":"string"},"merchantName":{"type":"string"},"merchantCategoryCode":{"type":"string"},"merchantCountryCode":{"type":"string"},"declinedReason":{"type":"string"}},"required":["cardId"]},"merchant":{"type":"object","properties":{"name":{"type":"string"},"logoUrl":{"type":"string"}},"required":["name"]},"signer":{"type":"object","properties":{"label":{"type":"string","description":"User label, or the derived provider name"},"kind":{"type":"string","enum":["primary","backup"]}},"required":["label","kind"]},"refundOf":{"type":["object","null"],"properties":{"id":{"type":"string"},"transactionHash":{"type":["string","null"]}},"required":["id","transactionHash"],"description":"The outbound transaction this row refunds (e.g. LiFi change, bridge refund); null otherwise."},"refunds":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"transactionHash":{"type":["string","null"]},"amount":{"type":"string"},"currency":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","transactionHash","amount","currency","createdAt"]},"description":"Completed refunds of this transaction, oldest first."}},"required":["typeGroup","type","direction","amount","currency","status","date"]}},"count":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"},"nextCursor":{"type":"string"}},"required":["data","count","limit","offset"]},"TransactionLite":{"type":"object","properties":{"hash":{"type":"string"},"direction":{"type":"string","enum":["outgoing","incoming","exchange"]},"amount":{"type":"string"},"currency":{"type":"string"},"status":{"type":"string"},"date":{"type":"string"},"counterparty":{"type":"object","properties":{"type":{"type":"string"},"name":{"type":"string"},"username":{"type":"string"}},"required":["type"]}},"required":["direction","amount","currency","status","date"]},"TransactionListResponseLite":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TransactionLite"}},"count":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"}},"required":["data","count","limit","offset"]},"UpdateCategoryResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]},"UpdateNoteResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]},"BatchCategoryResponse":{"type":"object","properties":{"succeeded":{"type":"number"},"failed":{"type":"number"},"results":{"type":"array","items":{"type":"object","properties":{"transactionHash":{"type":"string"},"success":{"type":"boolean"},"category":{"type":"string"},"error":{"type":"string"}},"required":["transactionHash","success"]}}},"required":["succeeded","failed","results"]},"BatchNoteResponse":{"type":"object","properties":{"succeeded":{"type":"number"},"failed":{"type":"number"},"results":{"type":"array","items":{"type":"object","properties":{"transactionHash":{"type":"string"},"success":{"type":"boolean"},"note":{"type":"string"},"error":{"type":"string"}},"required":["transactionHash","success"]}}},"required":["succeeded","failed","results"]},"SpendingByTypeGroup":{"type":"object","properties":{"period":{"type":"string"},"since":{"type":"string"},"typeGroups":{"type":"array","items":{"type":"object","properties":{"typeGroup":{"type":"string"},"name":{"type":"string"},"count":{"type":"number"},"total":{"type":"string"}},"required":["typeGroup","name","count","total"]}},"totalSpent":{"type":"string"},"transactionCount":{"type":"number"}},"required":["period","since","typeGroups","totalSpent","transactionCount"]},"DailySpending":{"type":"object","properties":{"dataPoints":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string"},"total":{"type":"string"}},"required":["day","total"]}}},"required":["dataPoints"]},"SpendingByCategory":{"type":"object","properties":{"buckets":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"count":{"type":"number"},"total":{"type":"string"}},"required":["category","count","total"]}}},"required":["buckets"]},"SpendingByContact":{"type":"object","properties":{"total":{"type":"string"},"count":{"type":"number"}},"required":["total","count"]},"SpendingByMerchant":{"type":"object","properties":{"buckets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["user","bank-withdrawal","external-wallet","coinbase","wallet-connect","other"]},"name":{"type":"string"},"count":{"type":"number"},"total":{"type":"string"}},"required":["id","type","name","count","total"]}}},"required":["buckets"]},"SpendingComparison":{"type":"object","properties":{"current":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"total":{"type":"string"},"count":{"type":"number"}},"required":["from","to","total","count"]},"previous":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"total":{"type":"string"},"count":{"type":"number"}},"required":["from","to","total","count"]},"deltaAbs":{"type":"string"},"deltaPct":{"type":["number","null"]}},"required":["current","previous","deltaAbs","deltaPct"]},"SpendingTransactions":{"type":"object","properties":{"transactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"senderId":{"type":"string"},"amount":{"type":"string"},"refundedAmount":{"type":"string","description":"Completed refunds linked to this transaction"},"netAmount":{"type":"string","description":"`amount` minus `refundedAmount`"},"currency":{"type":"string"},"transactionType":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string"},"recipient":{"type":"object","properties":{"userId":{"type":["string","null"]},"displayName":{"type":["string","null"]},"username":{"type":["string","null"]}},"required":["userId","displayName","username"]},"note":{"type":["string","null"]}},"required":["id","senderId","amount","refundedAmount","netAmount","currency","transactionType","status","createdAt","recipient","note"]}},"hasMore":{"type":"boolean"}},"required":["transactions","hasMore"]},"EarningsTimeSeries":{"type":"object","properties":{"timeSeries":{"type":"array","items":{"type":"object","properties":{"timestamp":{"type":"number"},"earnings":{"type":"number"},"balance":{"type":"number"}},"required":["timestamp","earnings","balance"]}},"currency":{"type":"string"},"totalEarnings":{"type":"number"},"netDeposits":{"type":"number"}},"required":["timeSeries","currency","totalEarnings","netDeposits"]},"ExchangeRate":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"rate":{"type":"string"},"updatedAt":{"type":"string"}},"required":["from","to","rate","updatedAt"]},"ApyRate":{"type":"object","properties":{"product":{"type":"string"},"currency":{"type":"string"},"apyPercent":{"type":"string"},"updatedAt":{"type":"string"}},"required":["product","currency","apyPercent","updatedAt"]},"DinariCapabilities":{"type":"object","properties":{"jurisdiction":{"type":"string","enum":["BASELINE","US"]},"canTrade":{"type":"boolean"},"canTradeRestrictedAssets":{"type":"boolean"},"canScheduleRecurringBuys":{"type":"boolean"},"canWrap":{"type":"boolean"},"canUseDefi":{"type":"boolean"},"requiresNbboQuote":{"type":"boolean"},"quoteMaxAgeSeconds":{"type":"number"},"requiresDedicatedWallet":{"type":"boolean"},"settlementStablecoins":{"type":"array","items":{"type":"string"}},"maxOrderNotionalUsd":{"type":["number","null"]},"minOrderNotionalUsd":{"type":"number"},"brokerOrderFeeUsd":{"type":"number"},"blockedReasons":{"type":"array","items":{"type":"string"}}},"required":["jurisdiction","canTrade","canTradeRestrictedAssets","canScheduleRecurringBuys","canWrap","canUseDefi","requiresNbboQuote","quoteMaxAgeSeconds","requiresDedicatedWallet","settlementStablecoins","maxOrderNotionalUsd","minOrderNotionalUsd","brokerOrderFeeUsd","blockedReasons"]},"DinariUsRequirements":{"type":"object","properties":{"jurisdiction":{"type":"string","enum":["BASELINE","US"]},"isUsCustomer":{"type":"boolean"},"canTrade":{"type":"boolean"},"blockedReasons":{"type":"array","items":{"type":"string"}},"consentVersion":{"type":"string"},"agentCollectableFields":{"type":"array","items":{"type":"string"}},"handoffRequiredSteps":{"type":"array","items":{"type":"string"}},"onboardingPath":{"type":"string"},"isComplete":{"type":"boolean"},"requirements":{"type":"object","properties":{"missingProfileFields":{"type":"array","items":{"type":"string"}},"missingConsentKeys":{"type":"array","items":{"type":"string"}},"missingRiskFormFields":{"type":"array","items":{"type":"string"}},"missingTaxId":{"type":"boolean"},"isComplete":{"type":"boolean"}},"required":["missingProfileFields","missingConsentKeys","missingRiskFormFields","missingTaxId","isComplete"]}},"required":["jurisdiction","isUsCustomer","canTrade","blockedReasons","consentVersion","agentCollectableFields","handoffRequiredSteps","onboardingPath","isComplete","requirements"]},"MarketHours":{"type":"object","properties":{"session":{"type":"string","enum":["pre_market","open","after_hours","closed"]},"source":{"type":"string","enum":["broker","computed"],"description":"Where the session came from, when the service reports it: the broker's own calendar or the computed fixed schedule. This endpoint reads the computed schedule and currently omits the field."},"open":{"type":"boolean"},"nextOpen":{"type":"string"},"exchange":{"type":"string","description":"The exchange described: \"US\" or an EODHD code."},"timezone":{"type":"string","description":"IANA zone the exchange keeps its hours in."},"etDate":{"type":"string","description":"Exchange-local date of now (yyyy-mm-dd). Eastern Time for the US."},"tradingDay":{"type":"boolean"},"holiday":{"type":["string","null"]},"cycleEtDate":{"type":"string"},"earlyClose":{"type":"boolean"},"windows":{"type":"array","items":{"type":"object","properties":{"session":{"type":"string","enum":["pre_market","open","after_hours","closed"]},"start":{"type":"string"},"end":{"type":"string"}},"required":["session","start","end"]}},"nextTransition":{"type":"object","properties":{"session":{"type":"string","enum":["pre_market","open","after_hours","closed"]},"at":{"type":"string"}},"required":["session","at"]}},"required":["session","open","exchange","timezone","etDate","tradingDay","holiday","cycleEtDate","earlyClose","windows","nextTransition"]},"FreeTradeAllowance":{"type":"object","properties":{"limit":{"type":["number","null"]},"used":{"type":"number"},"remaining":{"type":["number","null"]},"resetsAt":{"type":"string"},"degraded":{"type":"boolean"},"upgrade":{"type":["object","null"],"properties":{"planType":{"type":"string","enum":["STANDARD","PREMIUM","ULTRA"]},"freeTradesPerMonth":{"type":["number","null"]}},"required":["planType","freeTradesPerMonth"]}},"required":["limit","used","remaining","resetsAt","degraded","upgrade"]},"UpcomingIpo":{"type":"object","properties":{"cik":{"type":"string"},"name":{"type":"string"},"stage":{"type":"string","enum":["filed","priced","listed"]},"proposedSymbol":{"type":["string","null"]},"website":{"type":["string","null"]},"firstFiledAt":{"type":"string"},"lastFiledAt":{"type":"string"}},"required":["cik","name","stage","proposedSymbol","website","firstFiledAt","lastFiledAt"]},"UpcomingIpos":{"anyOf":[{"type":"object","properties":{"available":{"type":"boolean","enum":[true]},"ipos":{"type":"array","items":{"$ref":"#/components/schemas/UpcomingIpo"}},"featured":{"allOf":[{"$ref":"#/components/schemas/UpcomingIpo"},{"type":["object","null"]}]},"note":{"type":"string"}},"required":["available","ipos","featured","note"]},{"type":"object","properties":{"available":{"type":"boolean","enum":[false]},"reason":{"type":"string"}},"required":["available","reason"]}]},"WalletInfo":{"type":"object","properties":{"smartAccountAddress":{"type":"string"},"network":{"type":"string"},"supportedTokens":{"type":"array","items":{"type":"string"}},"username":{"type":"string"},"warning":{"type":"string"},"tip":{"type":"string"}},"required":["smartAccountAddress","network","supportedTokens","warning","tip"]},"WalletInfoLite":{"type":"object","properties":{"smartAccountAddress":{"type":"string"},"username":{"type":"string"}},"required":["smartAccountAddress"]},"LifetimeSavings":{"type":"object","properties":{"savings":{"type":"number"},"currencySymbol":{"type":"string"},"components":{"type":"object","properties":{"sponsoredGas":{"type":"number"},"ironFees":{"type":"number"},"tierDiscount":{"type":"number"}},"required":["sponsoredGas","ironFees","tierDiscount"]}},"required":["savings","currencySymbol","components"]},"VirtualAccounts":{"type":"object","properties":{"status":{"type":"string","enum":["active","pending","not_registered","unavailable"]},"accounts":{"type":"object","properties":{"eur":{"type":"object","properties":{"type":{"type":"string"},"iban":{"type":"string"},"canReceiveDeposits":{"type":"boolean"},"depositRestriction":{"type":"string"},"minimumTransfer":{"type":"string"},"firstPartyPayments":{"type":"string"},"thirdPartyPayments":{"type":"string"}},"required":["type","iban"]},"usd":{"type":"object","properties":{"type":{"type":"string"},"accountNumber":{"type":"string"},"routingNumber":{"type":"string"},"canReceiveDeposits":{"type":"boolean"},"depositRestriction":{"type":"string"},"minimumTransfer":{"type":"string"},"firstPartyPayments":{"type":"string"},"thirdPartyPayments":{"type":"string"}},"required":["type","accountNumber","routingNumber"]},"mxn":{"type":"object","properties":{"type":{"type":"string"},"clabe":{"type":"string"},"provider":{"type":"string","enum":["ETHERFUSE"]},"alias":{"type":"string"},"isActive":{"type":"boolean"},"canReceiveDeposits":{"type":"boolean"},"depositRestriction":{"type":"string"}},"required":["type","clabe","provider","isActive"]}}},"capabilityInfo":{"type":"object","properties":{"firstPartyPayments":{"type":"string"},"thirdPartyPayments":{"type":"string"}},"required":["firstPartyPayments","thirdPartyPayments"]},"message":{"type":"string","description":"Human-readable explanation when the accounts are not ready (not registered, still being set up, or unavailable)"},"stage":{"type":"string","description":"Onboarding stage with the banking partner while the accounts are still being set up"}},"required":["status"]},"VirtualAccountsLite":{"type":"object","properties":{"status":{"type":"string","enum":["active","pending","not_registered","unavailable"]},"accounts":{"type":"object","properties":{"eur":{"type":"object","properties":{"canReceiveDeposits":{"type":"boolean"},"depositRestriction":{"type":"string"},"iban":{"type":"string"}},"required":["iban"]},"usd":{"type":"object","properties":{"canReceiveDeposits":{"type":"boolean"},"depositRestriction":{"type":"string"},"accountNumber":{"type":"string"},"routingNumber":{"type":"string"}},"required":["accountNumber","routingNumber"]},"mxn":{"type":"object","properties":{"canReceiveDeposits":{"type":"boolean"},"depositRestriction":{"type":"string"},"clabe":{"type":"string"}},"required":["clabe"]}}}},"required":["status"]},"IronInformationRequest":{"type":"object","properties":{"id":{"type":"string"},"rfiType":{"type":"string"},"status":{"type":"string","enum":["waiting_on_response","needs_review","closed"]},"transactionId":{"type":["string","null"]},"sentAt":{"type":"string"},"openRound":{"type":["object","null"],"properties":{"id":{"type":"string"},"round_number":{"type":"number"},"questions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"prompt":{"type":"string"},"answer_type":{"type":"string","enum":["text","yes_no"]},"required":{"type":"boolean"},"transaction_id":{"type":["string","null"]}},"required":["id","prompt","answer_type","required"]}},"answers":{"type":["array","null"],"items":{"type":"object","properties":{"question_id":{"type":"string"},"text_value":{"type":"string"},"boolean_value":{"type":"boolean"},"explanation":{"type":"string"}},"required":["question_id"]}},"analyst_instructions":{"type":["string","null"]},"documents_requested":{"type":"boolean"},"document_instructions":{"type":["string","null"]},"document_upload_url":{"type":["string","null"]},"document_link_opened_at":{"type":["string","null"]},"sent_at":{"type":"string"},"response_received_at":{"type":["string","null"]}},"required":["id","round_number","questions","answers","analyst_instructions","documents_requested","document_instructions","document_upload_url","document_link_opened_at","sent_at","response_received_at"],"description":"The round currently awaiting the user's answers, or null when nothing is waiting on them"}},"required":["id","rfiType","status","transactionId","sentAt","openRound"]},"VirtualAccountLimits":{"type":"object","properties":{"limits":{"type":"array","items":{"type":"object","properties":{"direction":{"type":"string","enum":["deposit","withdrawal"]},"currency":{"type":"string"},"used":{"type":"number"},"threshold":{"type":["number","null"],"description":"null means no enforced cap, not unknown"},"remaining":{"type":["number","null"]},"periodStart":{"type":"string"},"periodEnd":{"type":"string"}},"required":["direction","currency","used","threshold","remaining","periodStart","periodEnd"]}},"eddRequired":{"type":"boolean","description":"Iron is requesting enhanced due diligence — transfers are paused until Source-of-Wealth proof is supplied"},"eddSubmitted":{"type":"boolean","description":"Proof is already with Iron compliance and under review"},"isReVerification":{"type":"boolean"},"canRequestEdd":{"type":"boolean","description":"The customer may start enhanced due diligence themselves to raise their limits (active, with no case already open)"},"ironStatus":{"type":["string","null"]},"rfis":{"type":"array","items":{"$ref":"#/components/schemas/IronInformationRequest"},"description":"Iron (MoonPay) information requests on this customer. Answers must come from the user in the Moneda app"},"rfiRequired":{"type":"boolean","description":"An information request is waiting on the user's answers (Settings > Features and limits in the app)"},"missingRequirements":{"type":"array","items":{"type":"string"},"description":"While eddRequired: Iron requirement codes still outstanding (e.g. kyc_questionnaire, kyc_proof_of_residence, kyc_identity, kyc_selfie). Empty otherwise or when Iron cannot be read. The user finishes them in the app"},"eddInFlight":{"type":"boolean","description":"While eddRequired: an identification is already in flight at Iron, so a new submission would be refused. False otherwise or when Iron cannot be read"}},"required":["limits","eddRequired","eddSubmitted","isReVerification","canRequestEdd","ironStatus","rfis","rfiRequired","missingRequirements","eddInFlight"]},"SettlementEstimate":{"type":"object","properties":{"sameBusinessDay":{"type":"boolean"},"settlesOn":{"type":"string","description":"YYYY-MM-DD in the bank's timezone (ET)"},"delayedByHoliday":{"type":"boolean","description":"A bank holiday, not just a weekend, pushed the date out"}},"required":["sameBusinessDay","settlesOn","delayedByHoliday"]},"ContactResponse":{"type":"object","properties":{"monedaContacts":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["user"]},"username":{"type":"string"},"displayName":{"type":["string","null"]},"isExternal":{"type":"boolean"},"lastInteraction":{"type":"string"}},"required":["type","displayName","isExternal","lastInteraction"]}},"walletContacts":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["wallet"]},"contactAccountId":{"type":"string"},"name":{"type":"string"},"address":{"type":"string"},"owner":{"type":"string"}},"required":["type","contactAccountId","name"]}},"bankContacts":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["bank"]},"contactAccountId":{"type":"string"},"name":{"type":"string"},"bankName":{"type":"string"},"iban":{"type":"string"},"accountNumber":{"type":"string"},"routingNumber":{"type":"string"},"country":{"type":"string"},"owner":{"type":"string"}},"required":["type","contactAccountId","name","country"]}},"monedaUsers":{"type":"number"},"wallets":{"type":"number"},"bankAccounts":{"type":"number"}},"required":["monedaContacts","walletContacts","bankContacts","monedaUsers","wallets","bankAccounts"]},"ContactResponseLite":{"type":"object","properties":{"monedaContacts":{"type":"array","items":{"type":"object","properties":{"username":{"type":"string"},"displayName":{"type":["string","null"]}},"required":["displayName"]}},"walletContacts":{"type":"array","items":{"type":"object","properties":{"contactAccountId":{"type":"string"},"name":{"type":"string"}},"required":["contactAccountId","name"]}},"bankContacts":{"type":"array","items":{"type":"object","properties":{"contactAccountId":{"type":"string"},"name":{"type":"string"},"bankName":{"type":"string"},"iban":{"type":"string"},"accountNumber":{"type":"string"},"routingNumber":{"type":"string"},"country":{"type":"string"}},"required":["contactAccountId","name","country"]}}},"required":["monedaContacts","walletContacts","bankContacts"]},"MyAccounts":{"type":"object","properties":{"wallets":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":"string"},"name":{"type":"string"},"address":{"type":"string"},"provider":{"type":"string"}},"required":["accountId","name","address","provider"]}},"bankAccounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"holderName":{"type":"string"},"bankName":{"type":"string"},"iban":{"type":"string"},"accountNumber":{"type":"string"},"routingNumber":{"type":"string"},"country":{"type":"string"}},"required":["id","holderName","bankName","country"]}},"walletCount":{"type":"number"},"bankCount":{"type":"number"}},"required":["wallets","bankAccounts","walletCount","bankCount"]},"MyAccountsLite":{"type":"object","properties":{"wallets":{"type":"array","items":{"type":"object","properties":{"accountId":{"type":"string"},"name":{"type":"string"}},"required":["accountId","name"]}},"bankAccounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"holderName":{"type":"string"},"bankName":{"type":"string"},"iban":{"type":"string"},"accountNumber":{"type":"string"},"routingNumber":{"type":"string"},"country":{"type":"string"}},"required":["id","holderName","bankName","country"]}}},"required":["wallets","bankAccounts"]},"RegisterWalletSaved":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"wallet":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"address":{"type":"string","description":"Masked address, not the full value"},"network":{"type":"string"},"supportedTokens":{"type":"array","items":{"type":"string"}}},"required":["id","name","address","network","supportedTokens"]}},"required":["success","wallet"]},"RegisterWalletConfirmationRequired":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"requiresConfirmation":{"type":"boolean","enum":[true]},"message":{"type":"string"}},"required":["success","requiresConfirmation","message"]},"RegisterWalletResponse":{"anyOf":[{"$ref":"#/components/schemas/RegisterWalletSaved"},{"$ref":"#/components/schemas/RegisterWalletConfirmationRequired"}]},"RegisterBankResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"bankAccount":{"type":"object","properties":{"id":{"type":"string"},"holderName":{"type":"string"},"country":{"type":"string"},"iban":{"type":"string","description":"Masked"},"accountNumber":{"type":"string","description":"Masked"},"routingNumber":{"type":"string","description":"Masked"}},"required":["id","holderName","country"]},"note":{"type":"string","description":"Human-readable outcome. Says so when registration with the banking partner was skipped or failed"}},"required":["success","bankAccount","note"]},"UserSettings":{"type":"object","properties":{"language":{"type":"string"},"currency":{"type":"string"},"country":{"type":["string","null"]},"phone":{"type":"string"},"phoneCountryCode":{"type":["string","null"]},"theme":{"type":"string"},"notifications":{"type":"object","properties":{"email":{"type":"boolean"},"inApp":{"type":"boolean"},"marketingEmails":{"type":"boolean"},"productUpdatesEmails":{"type":"boolean"},"marketingPush":{"type":"boolean"},"productUpdatesPush":{"type":"boolean"}},"required":["email","inApp","marketingEmails","productUpdatesEmails","marketingPush","productUpdatesPush"]},"biometrics":{"type":"boolean"},"animations":{"type":"boolean"},"decimalSeparator":{"type":"string"},"hideBalances":{"type":"boolean"},"helpImproveMoneda":{"type":"boolean","description":"Whether your Moneda AI conversations may be used to train Moneda's models. False means you turned it off. Always true on the free plan."}},"required":["language","currency","country","phone","phoneCountryCode","theme","notifications","biometrics","animations","decimalSeparator","hideBalances","helpImproveMoneda"]},"UserSettingsEmpty":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},"HelpImproveMoneda":{"type":"object","properties":{"helpImproveMoneda":{"type":"boolean"}},"required":["helpImproveMoneda"]},"UpdateSettings":{"type":"object","properties":{"decimalSeparator":{"type":"string","enum":["DOT","COMMA"],"description":"DOT for 1,234.56, COMMA for 1.234,56"},"marketingEmails":{"type":"boolean","description":"Marketing emails (offers, campaign reminders)"},"productUpdatesEmails":{"type":"boolean","description":"Product-update emails (new features)"},"marketingPush":{"type":"boolean","description":"Marketing push notifications"},"productUpdatesPush":{"type":"boolean","description":"Product-update push notifications"}}},"PasskeyList":{"type":"object","properties":{"passkeys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"target":{"type":"string","enum":["WALLET","LOGIN"],"description":"Pass this back to PATCH /v1/passkeys/{id}/label as `target`"},"label":{"type":["string","null"]},"status":{"type":"string"},"createdAt":{"type":"string"},"provider":{"type":"string","description":"Credential provider (iCloud Keychain, 1Password, …). Omitted on a LOGIN row whose provider is unknown — including any registered before Moneda recorded it, which cannot be backfilled."},"address":{"type":"string","description":"Masked Safe address"},"kind":{"type":"string","enum":["primary","backup"],"description":"primary = the shared-signer key from signup; backup = an extra key added later as a second Safe owner"},"onchainStatus":{"type":"string","description":"active | pending | failed | removed. A `pending` backup exists but its add-owner UserOp has not confirmed, so it cannot sign yet."},"pausedAt":{"type":["string","null"],"description":"WALLET rows only. ISO timestamp of when the key was paused, or null when it is not paused. Moneda refuses to relay a paused key's operations."},"credentialId":{"type":"string","description":"Masked WebAuthn credential id — never the full value"},"device":{"type":"string"},"lastUsedAt":{"type":"string"}},"required":["id","target","label","status","createdAt"]}},"count":{"type":"number"},"message":{"type":"string","description":"Present only when the list is empty"}},"required":["passkeys","count"]},"PasskeyListLite":{"type":"object","properties":{"passkeys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"target":{"type":"string","enum":["WALLET","LOGIN"]},"name":{"type":"string","description":"The label if set, otherwise the provider or device name"},"status":{"type":"string"},"kind":{"type":"string","enum":["primary","backup"]},"onchainStatus":{"type":"string"},"pausedAt":{"type":["string","null"]}},"required":["id","target","name","status"]}}},"required":["passkeys"]},"SetPasskeyLabelResponse":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]}},"required":["id","label"]},"ApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string","description":"First 12 characters of the key — enough to identify it"},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":["string","null"]},"expiresAt":{"type":["string","null"]},"revokedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"isActive":{"type":"boolean"}},"required":["id","name","prefix","scopes","lastUsedAt","expiresAt","revokedAt","createdAt","isActive"]},"CreatedApiKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"key":{"type":"string","description":"The secret. Shown ONCE, here — it is stored hashed and can never be retrieved again."},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expiresAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","name","key","prefix","scopes","expiresAt","createdAt"]},"CreateApiKeyRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"scopes":{"type":"array","items":{"type":"string"},"description":"Subset of the scopes your own credential holds. Allowed: read:account, read:balances, read:transactions, read:rates, read:points, read:referrals, read:virtual_accounts, read:contacts, read:settings, read:recovery, read:passkeys, read:external_accounts, read:knowledge, read:earnings, read:spending, read:users, read:payments, read:sub_accounts, read:notifications, read:receipts, read:subscriptions, read:scheduled_tx, write:profile, write:transactions, write:external_accounts, write:contacts"},"expiresInDays":{"type":"integer","exclusiveMinimum":0,"maximum":3650}},"required":["name","scopes"]},"RecoveryContacts":{"type":"object","properties":{"myContacts":{"type":"array","items":{"type":"object","properties":{"username":{"type":"string"},"displayName":{"type":"string"},"imageUrl":{"type":["string","null"]},"status":{"type":"string"},"addedAt":{"type":"string"}},"required":["username","displayName","imageUrl","status","addedAt"]}},"myContactsCount":{"type":"number"},"recoveryContactOf":{"type":"array","items":{"type":"object","properties":{"username":{"type":"string"},"displayName":{"type":"string"},"imageUrl":{"type":["string","null"]},"status":{"type":"string"},"addedAt":{"type":"string"}},"required":["username","displayName","imageUrl","status","addedAt"]}},"recoveryContactOfCount":{"type":"number"},"message":{"type":"string"}},"required":["myContacts","myContactsCount","recoveryContactOf","recoveryContactOfCount"]},"RecoveryContactsLite":{"type":"object","properties":{"myContacts":{"type":"array","items":{"type":"object","properties":{"username":{"type":"string"},"displayName":{"type":"string"},"status":{"type":"string"},"addedAt":{"type":"string"}},"required":["username","displayName","status","addedAt"]}},"recoveryContactOf":{"type":"array","items":{"type":"object","properties":{"username":{"type":"string"},"displayName":{"type":"string"},"status":{"type":"string"},"addedAt":{"type":"string"}},"required":["username","displayName","status","addedAt"]}}},"required":["myContacts","recoveryContactOf"]},"PointsBalance":{"type":"object","properties":{"totalPoints":{"type":"number"}},"required":["totalPoints"]},"PointsActivityResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"points":{"type":"number"},"description":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["actionType","points","description","createdAt"]}},"count":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"}},"required":["data","count","limit","offset"]},"PointsActivityResponseLite":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"actionType":{"type":"string"},"points":{"type":"number"},"date":{"type":"string"}},"required":["actionType","points","date"]}},"count":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"}},"required":["data","count","limit","offset"]},"PointsRedemptionConfig":{"type":"object","properties":{"pointsPerCreditUsd":{"type":"number"},"minPoints":{"type":"number"},"monthlyCapUsd":{"type":"string"},"estMessageUsd":{"type":"number"},"totalPoints":{"type":"number"},"monthlyRedeemedUsd":{"type":"string"},"monthlyRemainingUsd":{"type":"string"},"monthlyResetsAt":{"type":"string"}},"required":["pointsPerCreditUsd","minPoints","monthlyCapUsd","estMessageUsd","totalPoints","monthlyRedeemedUsd","monthlyRemainingUsd","monthlyResetsAt"]},"PointsRedeemResult":{"type":"object","properties":{"granted":{"type":"boolean","description":"False on an idempotent replay (same requestId)."},"pointsRedeemed":{"type":"number"},"budgetUsd":{"type":"string","description":"AI budget granted (USD)."},"paidBalanceEur":{"type":"string"},"totalPoints":{"type":"number"},"creditBalanceUsd":{"type":"string"}},"required":["granted","pointsRedeemed","budgetUsd","paidBalanceEur","totalPoints","creditBalanceUsd"]},"ReferralInfo":{"type":"object","properties":{"code":{"type":"string"},"link":{"type":"string"},"referralCount":{"type":"number"}},"required":["code","link","referralCount"]},"ReferralInfoLite":{"type":"object","properties":{"code":{"type":"string"},"referralCount":{"type":"number"}},"required":["code","referralCount"]},"RefereesResponse":{"type":"object","properties":{"referees":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"status":{"type":"string"},"referredAt":{"type":"string"}},"required":["name","email","status","referredAt"]}},"count":{"type":"number"}},"required":["referees","count"]},"RefereesResponseLite":{"type":"object","properties":{"referees":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"referredAt":{"type":"string"}},"required":["name","referredAt"]},"description":"At most the 25 most recent referees. `count` is still the true total"},"count":{"type":"number"}},"required":["referees","count"]},"KnowledgeSearchResponse":{"type":"object","properties":{"query":{"type":"string"},"totalMatches":{"type":"number"},"returned":{"type":"number"},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"category":{"type":"string"},"relatedIds":{"type":"array","items":{"type":"string"}}},"required":["id","question","answer","category"]}}},"required":["query","totalMatches","returned","results"]},"KnowledgeCategoriesResponse":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"itemCount":{"type":"number"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"}},"required":["id","question"]}}},"required":["slug","name","itemCount","items"]}}},"required":["categories"]},"KnowledgeCategoryResponse":{"type":"object","properties":{"category":{"type":"string"},"slug":{"type":"string"},"itemCount":{"type":"number"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"category":{"type":"string"},"relatedIds":{"type":"array","items":{"type":"string"}}},"required":["id","question","answer","category"]}}},"required":["category","slug","itemCount","items"]},"KnowledgeItem":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string"},"category":{"type":"string","description":"Category name"},"categorySlug":{"type":"string"},"relatedItems":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"category":{"type":"string","description":"Category slug of the related item"}},"required":["id","question","category"]}}},"required":["id","question","answer","category","categorySlug"]},"UserSearchResponse":{"type":"object","properties":{"users":{"type":"array","items":{"type":"object","properties":{"username":{"type":"string"},"displayName":{"type":"string"}},"required":["username","displayName"]}},"count":{"type":"number"}},"required":["users","count"]},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]},"PaymentResponse":{"type":"object","properties":{"paymentRequestId":{"type":"string"},"status":{"type":"string"},"amount":{"type":"string"},"currency":{"type":"string"},"recipient":{"type":"string"},"paymentType":{"type":"string"},"expiresAt":{"type":"string"},"notificationSent":{"type":"boolean","description":"Whether the approval push went out. False is not a failure: the request stands and delivery is retried."},"message":{"type":"string"},"nextStep":{"type":"string"},"pollAfterMs":{"type":"number"},"transactionId":{"type":"string"},"transactionHash":{"type":"string"},"error":{"type":"string"},"legs":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"status":{"type":"string","enum":["placed","pending","failed"]}},"required":["symbol","status"]},"description":"Only for a portfolio_buy request: the outcome of each leg."}},"required":["paymentRequestId","status","amount","currency","recipient","paymentType","expiresAt","message"]},"DestinationRailField":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"enum":{"type":"array","items":{"type":"string"}},"optional":{"type":"boolean"}},"required":["name","description"]},"DestinationRail":{"type":"object","properties":{"rail":{"type":"string"},"label":{"type":"string"},"requiredFields":{"type":"array","items":{"$ref":"#/components/schemas/DestinationRailField"}}},"required":["rail","label","requiredFields"]},"PaymentDestination":{"type":"object","properties":{"country":{"type":"string"},"currency":{"type":"string"},"rails":{"type":"array","items":{"$ref":"#/components/schemas/DestinationRail"}}},"required":["country","currency","rails"]},"PaymentQuote":{"type":"object","properties":{"quoteId":{"type":["string","null"]},"fromCurrency":{"type":"string"},"toCurrency":{"type":"string"},"direction":{"type":"string","enum":["ONRAMP","OFFRAMP"]},"amountIn":{"type":"number"},"amountOut":{"type":"number"},"exchangeRate":{"type":"number"},"fee":{"type":["number","null"]},"taxes":{"type":["number","null"]},"validForSeconds":{"type":["number","null"]},"validUntil":{"type":["string","null"]},"fiatCurrency":{"type":"string"}},"required":["quoteId","fromCurrency","toCurrency","direction","amountIn","amountOut","exchangeRate","fee","taxes","validForSeconds","validUntil","fiatCurrency"]},"StartSessionResponse":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid"}},"required":["sessionId"]},"EndSessionResponse":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]},"ListActiveSessionsResponse":{"type":"object","properties":{"loginSessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"device":{"type":["string","null"]},"appVersion":{"type":["string","null"]},"city":{"type":["string","null"]},"countryCode":{"type":["string","null"]},"lastActive":{"type":"string"}},"required":["id","type","device","appVersion","city","countryCode","lastActive"]}},"connectedAgents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"enabled":{"type":"boolean"},"connectedAt":{"type":"string"},"lastActive":{"type":["string","null"]}},"required":["id","name","enabled","connectedAt","lastActive"]}}},"required":["loginSessions","connectedAgents"]},"RevokeAllSessionsResponse":{"type":"object","properties":{"revoked":{"type":"number"}},"required":["revoked"]},"SubAccountSummary":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["DEPLOYING","ACTIVE","FROZEN","CLOSING","CLOSED"]},"type":{"type":"string","enum":["CHECKING","SAVINGS"]},"enableWalletConnect":{"type":"boolean"},"childSafeAddress":{"type":"string"},"icon":{"type":["string","null"]},"avatarColor":{"type":["string","null"]},"isCreator":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","name","status","type","enableWalletConnect","childSafeAddress","icon","avatarColor","isCreator","createdAt"]},"SubAccountSummaryLite":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","description":"Same values as the full view's `status`."},"type":{"type":"string","description":"Same values as the full view's `type`."}},"required":["id","name","status","type"]},"ListSubAccountsResponse":{"type":"object","properties":{"subAccounts":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/SubAccountSummary"}},{"type":"array","items":{"$ref":"#/components/schemas/SubAccountSummaryLite"}}]}},"required":["subAccounts"]},"SubAccountMember":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string"},"displayName":{"type":"string"},"username":{"type":"string"},"isCreator":{"type":"boolean"},"state":{"type":"string","enum":["INVITED","PENDING_ON_CHAIN","ACTIVE"]}},"required":["userId","role","displayName","username","isCreator","state"]},"SubAccountDetail":{"allOf":[{"$ref":"#/components/schemas/SubAccountSummary"},{"type":"object","properties":{"parentSafeAddress":{"type":"string"},"index":{"type":"integer"},"members":{"type":"array","items":{"$ref":"#/components/schemas/SubAccountMember"}},"threshold":{"type":"integer","description":"Approvals needed to move money (1 = single-signature)."},"closedAt":{"type":["string","null"]}},"required":["parentSafeAddress","index","members","threshold","closedAt"]}]},"SubAccountDetailLite":{"allOf":[{"$ref":"#/components/schemas/SubAccountSummaryLite"},{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string"}},"required":["userId","role"]}},"closedAt":{"type":["string","null"]}},"required":["members","closedAt"]}]},"SubAccountBalance":{"type":"object","properties":{"subAccountId":{"type":"string"},"childSafeAddress":{"type":"string"},"status":{"type":"string","enum":["DEPLOYING","ACTIVE","FROZEN","CLOSING","CLOSED"]},"balances":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string","enum":["USDC","EURC","CHFAU","CADD","MXNB"]},"balance":{"type":"string"}},"required":["currency","balance"]}}},"required":["subAccountId","childSafeAddress","status","balances"]},"SubAccountBalanceLite":{"type":"object","properties":{"subAccountId":{"type":"string"},"status":{"type":"string","description":"Same values as the full view's `status`."},"balances":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"balance":{"type":"string"}},"required":["currency","balance"]}}},"required":["subAccountId","status","balances"]},"TotalWealth":{"type":"object","properties":{"main":{"type":"object","properties":{"walletUsdc":{"type":"string"},"walletEurc":{"type":"string"},"walletChfau":{"type":"string"},"walletCadd":{"type":"string"},"walletMxnb":{"type":"string"},"morphoUsdc":{"type":"string"},"morphoEurc":{"type":"string"},"morphoMxnb":{"type":"string"},"yoUsdc":{"type":"string"},"yoEurc":{"type":"string"}},"required":["walletUsdc","walletEurc","walletChfau","walletCadd","walletMxnb","morphoUsdc","morphoEurc","morphoMxnb","yoUsdc","yoEurc"]},"subAccounts":{"type":"array","items":{"type":"object","properties":{"walletUsdc":{"type":"string"},"walletEurc":{"type":"string"},"walletChfau":{"type":"string"},"walletCadd":{"type":"string"},"walletMxnb":{"type":"string"},"morphoUsdc":{"type":"string"},"morphoEurc":{"type":"string"},"morphoMxnb":{"type":"string"},"yoUsdc":{"type":"string"},"yoEurc":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","enum":["DEPLOYING","ACTIVE","FROZEN","CLOSING","CLOSED"]}},"required":["walletUsdc","walletEurc","walletChfau","walletCadd","walletMxnb","morphoUsdc","morphoEurc","morphoMxnb","yoUsdc","yoEurc","id","name","status"]}},"totals":{"type":"object","properties":{"usdc":{"type":"string"},"eurc":{"type":"string"},"chfau":{"type":"string"},"cadd":{"type":"string"},"mxnb":{"type":"string"}},"required":["usdc","eurc","chfau","cadd","mxnb"]},"stocksUsd":{"type":["string","null"]},"cryptoUsd":{"type":["string","null"]},"stockPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"quantity":{"type":"string"},"valueUsd":{"type":"number"},"costUsd":{"type":["number","null"]},"realizedUsd":{"type":["number","null"]}},"required":["symbol","quantity","valueUsd","costUsd","realizedUsd"]}},"cryptoPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"quantity":{"type":"string"},"valueUsd":{"type":"number"},"costUsd":{"type":["number","null"]},"realizedUsd":{"type":["number","null"]}},"required":["symbol","quantity","valueUsd","costUsd","realizedUsd"]}},"closedStockPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"realizedUsd":{"type":"number"}},"required":["symbol","realizedUsd"]}},"closedCryptoPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"realizedUsd":{"type":"number"}},"required":["symbol","realizedUsd"]}},"otherCryptoPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"quantity":{"type":"string"},"valueUsd":{"type":["number","null"]}},"required":["symbol","quantity","valueUsd"]}},"totalWealthUsd":{"type":["string","null"]},"exchangeRate":{"type":"number"}},"required":["main","subAccounts","totals","stocksUsd","cryptoUsd","stockPositions","cryptoPositions","closedStockPositions","closedCryptoPositions","otherCryptoPositions","totalWealthUsd","exchangeRate"]},"TotalWealthLite":{"type":"object","properties":{"main":{"type":"object","properties":{"walletUsdc":{"type":"string"},"walletEurc":{"type":"string"},"walletChfau":{"type":"string"},"walletCadd":{"type":"string"},"walletMxnb":{"type":"string"}},"required":["walletUsdc","walletEurc","walletChfau","walletCadd","walletMxnb"]},"subAccounts":{"type":"array","items":{"type":"object","properties":{"walletUsdc":{"type":"string"},"walletEurc":{"type":"string"},"walletChfau":{"type":"string"},"walletCadd":{"type":"string"},"walletMxnb":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string","description":"Same values as the full view's `status`."}},"required":["walletUsdc","walletEurc","walletChfau","walletCadd","walletMxnb","id","name","status"]}},"totals":{"type":"object","properties":{"usdc":{"type":"string"},"eurc":{"type":"string"},"chfau":{"type":"string"},"cadd":{"type":"string"},"mxnb":{"type":"string"}},"required":["usdc","eurc","chfau","cadd","mxnb"]},"stocksUsd":{"type":["string","null"]},"cryptoUsd":{"type":["string","null"]},"stockPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"quantity":{"type":"string"},"valueUsd":{"type":"number"},"costUsd":{"type":["number","null"]},"realizedUsd":{"type":["number","null"]}},"required":["symbol","quantity","valueUsd","costUsd","realizedUsd"]}},"cryptoPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"quantity":{"type":"string"},"valueUsd":{"type":"number"},"costUsd":{"type":["number","null"]},"realizedUsd":{"type":["number","null"]}},"required":["symbol","quantity","valueUsd","costUsd","realizedUsd"]}},"closedStockPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"realizedUsd":{"type":"number"}},"required":["symbol","realizedUsd"]}},"closedCryptoPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"realizedUsd":{"type":"number"}},"required":["symbol","realizedUsd"]}},"otherCryptoPositions":{"type":"array","items":{"type":"object","properties":{"symbol":{"type":"string"},"quantity":{"type":"string"},"valueUsd":{"type":["number","null"]}},"required":["symbol","quantity","valueUsd"]}},"totalWealthUsd":{"type":["string","null"]}},"required":["main","subAccounts","totals","stocksUsd","cryptoUsd","stockPositions","cryptoPositions","closedStockPositions","closedCryptoPositions","otherCryptoPositions","totalWealthUsd"]},"JointProposalSummary":{"type":"object","properties":{"id":{"type":"string"},"subAccountId":{"type":"string"},"type":{"type":"string"},"status":{"type":"string"},"description":{"type":["string","null"]},"amount":{"type":["string","null"]},"currency":{"type":["string","null"]},"recipientLabel":{"type":["string","null"]},"threshold":{"type":"number"},"approvedCount":{"type":"number"},"rejectedCount":{"type":"number"},"proposedBy":{"type":"string"},"proposerName":{"type":["string","null"]},"isExpired":{"type":"boolean"},"expiresAt":{"type":["string","null"]},"executedAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","subAccountId","type","status","description","amount","currency","recipientLabel","threshold","approvedCount","rejectedCount","proposedBy","proposerName","isExpired","expiresAt","executedAt","createdAt"]},"ListSubAccountProposalsResponse":{"type":"object","properties":{"proposals":{"type":"array","items":{"$ref":"#/components/schemas/JointProposalSummary"}}},"required":["proposals"]},"JointProposalDetail":{"allOf":[{"$ref":"#/components/schemas/JointProposalSummary"},{"type":"object","properties":{"approvals":{"type":"array","items":{"type":"object","properties":{"userId":{"type":"string"},"displayName":{"type":["string","null"]},"approved":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["userId","displayName","approved","createdAt"]}}},"required":["approvals"]}]},"SubscriptionChargeItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"createdAt":{"type":"string"},"senderId":{"type":["string","null"]},"amount":{"type":["string","null"]},"currency":{"type":"string"},"status":{"type":"string"},"scheduledTransactionId":{"type":["string","null"],"format":"uuid"},"transactionHash":{"type":["string","null"]},"planType":{"type":["string","null"]},"billingPeriod":{"type":["string","null"]},"periodStart":{"type":["string","null"]},"periodEnd":{"type":["string","null"]},"invoiceNumber":{"type":["string","null"]},"invoiceIssuedAt":{"type":["string","null"]},"source":{"type":"string","enum":["balance","card"]},"invoiceUrl":{"type":["string","null"]},"cardBrand":{"type":["string","null"]},"cardLast4":{"type":["string","null"]},"isProration":{"type":"boolean"}},"required":["id","createdAt","senderId","amount","currency","status","scheduledTransactionId","transactionHash","planType","billingPeriod","periodStart","periodEnd","invoiceNumber","invoiceIssuedAt","source","invoiceUrl","cardBrand","cardLast4","isProration"]},"SubscriptionChargeHistoryPage":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/SubscriptionChargeItem"}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"]},"SubscriptionInvoiceResult":{"type":"object","properties":{"invoiceNumber":{"type":"string"},"downloadUrl":{"type":"string","format":"uri"}},"required":["invoiceNumber","downloadUrl"]},"ScheduledTransactionSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING_ACTIVATION","ACTIVE","PAUSED","EXPIRED","CANCELLED","FAILED_ACTIVATION"]},"label":{"type":"string"},"paymentType":{"type":"string"},"frequency":{"type":"string","enum":["DAILY","WEEKLY","BIWEEKLY","MONTHLY","QUARTERLY","CUSTOM"]},"customIntervalDays":{"type":["integer","null"]},"amount":{"type":"string","description":"Raw token units (6-decimal stablecoins)"},"currency":{"type":"string"},"recipientLabel":{"type":"string"},"recipientAccountId":{"type":["string","null"],"format":"uuid"},"recipientUserId":{"type":["string","null"],"format":"uuid"},"startDate":{"type":"string"},"endDate":{"type":["string","null"]},"executionDayOfMonth":{"type":["integer","null"],"minimum":1,"maximum":28},"executionDayOfWeek":{"type":["integer","null"],"minimum":0,"maximum":6},"executionTimeLocal":{"type":"string"},"userTimezone":{"type":"string"},"nextExecutionAt":{"type":["string","null"]},"sessionExpiresAt":{"type":["string","null"]},"sessionDurationDays":{"type":["integer","null"]},"totalExecutions":{"type":"integer"},"failedExecutions":{"type":"integer"},"createdAt":{"type":"string"},"pausedAt":{"type":["string","null"]},"pauseReason":{"type":["string","null"]}},"required":["id","status","label","paymentType","frequency","customIntervalDays","amount","currency","recipientLabel","recipientAccountId","recipientUserId","startDate","endDate","executionDayOfMonth","executionDayOfWeek","executionTimeLocal","userTimezone","nextExecutionAt","sessionExpiresAt","sessionDurationDays","totalExecutions","failedExecutions","createdAt","pausedAt","pauseReason"]},"ListScheduledTransactionsResponse":{"type":"object","properties":{"schedules":{"type":"array","items":{"$ref":"#/components/schemas/ScheduledTransactionSummary"}}},"required":["schedules"]},"UpcomingScheduledExecution":{"type":"object","properties":{"scheduleId":{"type":"string","format":"uuid"},"label":{"type":"string"},"amount":{"type":"string"},"currency":{"type":"string"},"recipientLabel":{"type":"string"},"scheduledFor":{"type":"string"}},"required":["scheduleId","label","amount","currency","recipientLabel","scheduledFor"]},"ListUpcomingScheduledExecutionsResponse":{"type":"object","properties":{"executions":{"type":"array","items":{"$ref":"#/components/schemas/UpcomingScheduledExecution"}}},"required":["executions"]},"ScheduledExecution":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"scheduledFor":{"type":"string"},"status":{"type":"string","enum":["PENDING","COMPLETED","COMPLETED_DELAYED","FAILED","MISSED"]},"transactionOpHash":{"type":["string","null"]},"transactionHash":{"type":["string","null"]},"errorMessage":{"type":["string","null"]},"amount":{"type":"string"},"currency":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","scheduledFor","status","transactionOpHash","transactionHash","errorMessage","amount","currency","createdAt"]},"ScheduledTransactionHistoryPage":{"type":"object","properties":{"scheduleId":{"type":"string","format":"uuid"},"executions":{"type":"array","items":{"$ref":"#/components/schemas/ScheduledExecution"}},"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"]}},"required":["scheduleId","executions","hasMore","nextCursor"]},"PauseScheduledTransactionResponse":{"type":"object","properties":{"scheduleId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PAUSED"]}},"required":["scheduleId","status"]},"ResumeScheduledTransactionResponse":{"type":"object","properties":{"scheduleId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["ACTIVE"]},"nextExecutionAt":{"type":"string","format":"date-time"}},"required":["scheduleId","status","nextExecutionAt"]},"ScheduledPaymentProposal":{"type":"object","properties":{"scheduleId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING_ACTIVATION"]},"amount":{"type":"string"},"currency":{"type":"string"},"frequency":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":["string","null"]},"recipient":{"type":"string"},"expiresAt":{"type":"string"},"notificationSent":{"type":"boolean"},"message":{"type":"string"},"nextStep":{"type":"string"}},"required":["scheduleId","status","amount","currency","frequency","startDate","endDate","recipient","expiresAt","notificationSent","message","nextStep"]},"ConditionalOrder":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"ruleId":{"type":"string","format":"uuid","description":"Groups the repeats of a repeating rule; a one-shot order is a rule of one."},"sequence":{"type":"integer","description":"1-based repeat number within the rule."},"repeatCount":{"type":"integer","description":"How many repeats the rule authorised (1 = one-shot)."},"assetSymbol":{"type":"string"},"assetAddress":{"type":"string","description":"The asset's Base contract address."},"side":{"type":"string","enum":["BUY","SELL"]},"type":{"type":"string","enum":["LIMIT","STOP_LOSS","TAKE_PROFIT","TRAILING_STOP"]},"status":{"type":"string","enum":["PENDING_ACTIVATION","ARMED","PUBLISHED","FILLED","CANCELLED","EXPIRED","WAITING"]},"triggerPrice":{"type":["string","null"]},"trailPercent":{"type":["string","null"]},"highWaterMark":{"type":["string","null"]},"slippageBps":{"type":"integer"},"sellToken":{"type":"string"},"buyToken":{"type":"string"},"sellAmount":{"type":"string","description":"Raw token units of the sold leg"},"minBuyAmount":{"type":"string","description":"Raw token units, minimum received"},"feeAmount":{"type":"string"},"feeToken":{"type":"string"},"expiresAt":{"type":"string"},"orderUid":{"type":"string"},"triggeredPrice":{"type":["string","null"]},"executedBuyAmount":{"type":["string","null"]},"skipReason":{"type":["string","null"]},"createdAt":{"type":"string"},"publishedAt":{"type":["string","null"]},"filledAt":{"type":["string","null"]}},"required":["id","ruleId","sequence","repeatCount","assetSymbol","assetAddress","side","type","status","triggerPrice","trailPercent","highWaterMark","slippageBps","sellToken","buyToken","sellAmount","minBuyAmount","feeAmount","feeToken","expiresAt","orderUid","triggeredPrice","executedBuyAmount","skipReason","createdAt","publishedAt","filledAt"]},"ListConditionalOrdersResponse":{"type":"object","properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/ConditionalOrder"}}},"required":["orders"]},"RequestStatementResponse":{"type":"object","properties":{"requestId":{"type":"string","format":"uuid"},"status":{"type":"string"},"kind":{"type":"string","enum":["balance_statement"]},"reportingCurrency":{"type":"string","enum":["USD","EUR","CHF","CAD"]},"currency":{"type":["string","null"],"enum":["USD","EUR","CHF","CAD","MXN","CRYPTO"]},"requestedAt":{"type":"string"}},"required":["requestId","status","kind","reportingCurrency","currency","requestedAt"]},"RequestStatementBody":{"type":"object","properties":{"kind":{"type":"string","enum":["balance_statement"]},"year":{"type":"integer","minimum":2020,"maximum":2100,"description":"Tax year (Jan 1 – Dec 31 UTC). Use this OR from/to, not both."},"from":{"type":"string","description":"ISO-8601 inclusive start (UTC). Required if `year` is omitted. Not before 2020-01-01."},"to":{"type":"string","description":"ISO-8601 exclusive end (UTC). Defaults to now; a future date is clamped to now. The period can cover at most 366 days."},"currency":{"type":["string","null"],"enum":["USD","EUR","CHF","CAD","MXN","CRYPTO"],"description":"Statement filter: USD, EUR, CHF, CAD, MXN or CRYPTO. CRYPTO lists crypto and stock trades; the currency filters leave those out, and an exchange between two currencies is on the statement of both. Omit or null for everything."},"reportingCurrency":{"type":"string","enum":["USD","EUR","CHF","CAD"],"description":"Currency the amounts are valued in. Defaults to `currency` when it is USD, EUR, CHF or CAD, then the user's display currency, then USD."}},"required":["kind"]},"StatementStatus":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","description":"Statement kind (currently only balance_statement)."},"status":{"type":"string","description":"REQUESTED → PROCESSING → READY → DOWNLOADED, plus terminal EXPIRED / FAILED."},"fileSizeBytes":{"type":["integer","null"]},"requestedAt":{"type":"string"},"readyAt":{"type":["string","null"]},"downloadedAt":{"type":["string","null"]},"expiresAt":{"type":["string","null"],"description":"When the current presigned download URL stops working."},"downloadUrl":{"type":["string","null"],"description":"Time-limited PDF URL. Populated while `status` is READY or DOWNLOADED and the statement has not expired; freshly presigned on every read."},"errorMessage":{"type":["string","null"],"description":"Fixed code when `status` is FAILED (`generation_failed` or `generation_timed_out`), otherwise null. Never raw error text."},"periodFrom":{"type":"string","description":"ISO-8601 inclusive start of the statement period."},"periodTo":{"type":"string","description":"ISO-8601 exclusive end of the statement period."},"currency":{"type":["string","null"],"enum":["USD","EUR","CHF","CAD","MXN","CRYPTO"],"description":"Statement filter from the request; null means everything."}},"required":["id","kind","status","fileSizeBytes","requestedAt","readyAt","downloadedAt","expiresAt","downloadUrl","errorMessage","periodFrom","periodTo","currency"]},"ListStatementsResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/StatementStatus"}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"]},"StatementStatusResponse":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","description":"Statement kind (currently only balance_statement)."},"status":{"type":"string","description":"REQUESTED → PROCESSING → READY → DOWNLOADED, plus terminal EXPIRED / FAILED."},"fileSizeBytes":{"type":["integer","null"]},"requestedAt":{"type":"string"},"readyAt":{"type":["string","null"]},"downloadedAt":{"type":["string","null"]},"expiresAt":{"type":["string","null"],"description":"When the current presigned download URL stops working."},"downloadUrl":{"type":["string","null"],"description":"Time-limited PDF URL. Populated while `status` is READY or DOWNLOADED and the statement has not expired; freshly presigned on every read."},"errorMessage":{"type":["string","null"],"description":"Fixed code when `status` is FAILED (`generation_failed` or `generation_timed_out`), otherwise null. Never raw error text."},"periodFrom":{"type":"string","description":"ISO-8601 inclusive start of the statement period."},"periodTo":{"type":"string","description":"ISO-8601 exclusive end of the statement period."},"currency":{"type":["string","null"],"enum":["USD","EUR","CHF","CAD","MXN","CRYPTO"],"description":"Statement filter from the request; null means everything."}},"required":["id","kind","status","fileSizeBytes","requestedAt","readyAt","downloadedAt","expiresAt","downloadUrl","errorMessage","periodFrom","periodTo","currency"]},"MarkStatementDownloadedResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"alreadyMarked":{"type":"boolean"}},"required":["success"]},"MarkStatementDownloadedBody":{"type":"object","properties":{"requestId":{"type":"string","format":"uuid","description":"Target the exact statement the user opened (preferred — kind/currency can ambiguate when the UI filter changes between tap and mark)."},"kind":{"type":"string","enum":["balance_statement"]},"currency":{"type":["string","null"],"enum":["USD","EUR","CHF","CAD","MXN","CRYPTO"],"description":"Without `requestId`: a filter for that filter's statements, null for all-activity statements only, omit for any. USD, EUR, CHF, CAD, MXN or CRYPTO. CRYPTO lists crypto and stock trades; the currency filters leave those out, and an exchange between two currencies is on the statement of both."}}},"CreateAgentSessionResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING_ACTIVATION"]},"childSafeAddress":{"type":"string"},"activationUrl":{"type":"string"},"claimToken":{"type":"string"},"pollAfterMs":{"type":"integer"}},"required":["id","status","childSafeAddress","activationUrl","claimToken","pollAfterMs"]},"CreateAgentSessionRequest":{"type":"object","properties":{"recipient":{"type":"string","description":"The single pinned crypto recipient (0x…)."},"currency":{"type":"string","enum":["USD","EUR"]},"maxAmountPerExecution":{"type":"string","description":"Per-execution cap in user-facing units, e.g. '25.00'."},"maxAmountPerPeriod":{"type":"string","description":"Per-period cap in user-facing units, e.g. '100.00'."},"periodSeconds":{"type":"integer","exclusiveMinimum":0},"expiresAt":{"type":"string","format":"date-time"},"fundingAmount":{"type":"string","description":"Optional initial parent→child funding, user-facing units."}},"required":["recipient","currency","maxAmountPerExecution","maxAmountPerPeriod","periodSeconds"]},"ExecuteAgentSessionResponse":{"type":"object","properties":{"transactionId":{"type":"string","format":"uuid"},"status":{"type":"string"},"transactionOpHash":{"type":["string","null"]},"idempotent":{"type":"boolean"}},"required":["transactionId","status","transactionOpHash","idempotent"]},"ExecuteAgentSessionRequest":{"type":"object","properties":{"recipient":{"type":"string","description":"Must match the session's pinned recipient (0x…)."},"amount":{"type":"string","description":"Amount in user-facing units, e.g. '10.00'."},"currency":{"type":"string","enum":["USD","EUR"]},"idempotencyKey":{"type":"string","minLength":1,"maxLength":255,"description":"Stable key for safe retries — reuse the same key to retry a payment you're unsure completed; the original transaction is returned rather than sent twice."}},"required":["recipient","amount","currency"]},"AgentSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING_ACTIVATION","ACTIVE","PAUSED","REVOKED","EXPIRED","FAILED_ACTIVATION"]},"policyMode":{"type":"string"},"token":{"type":"string"},"childSafeAddress":{"type":"string"},"childSafeDeployed":{"type":"boolean"},"recipientAllowlist":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"token":{"type":"string"}},"required":["address"]}},"maxAmountPerExecution":{"type":["string","null"],"description":"Raw token units (per-execution cap)"},"maxAmountPerPeriod":{"type":["string","null"],"description":"Raw token units (per-period cap)"},"periodSeconds":{"type":["integer","null"]},"periodSpent":{"type":"string","description":"Raw token units spent this period"},"remainingPeriodCap":{"type":["string","null"]},"periodResetAt":{"type":["string","null"]},"expiresAt":{"type":["string","null"]},"boundClientName":{"type":["string","null"]},"boundProviderName":{"type":["string","null"]},"consumerKind":{"type":"string"},"permissionId":{"type":["string","null"]},"totalExecutions":{"type":"integer"},"failedExecutions":{"type":"integer"},"pauseReason":{"type":["string","null"]},"activatedAt":{"type":["string","null"]},"revokedAt":{"type":["string","null"]},"createdAt":{"type":["string","null"]}},"required":["id","status","policyMode","token","childSafeAddress","childSafeDeployed","recipientAllowlist","maxAmountPerExecution","maxAmountPerPeriod","periodSeconds","periodSpent","remainingPeriodCap","periodResetAt","expiresAt","boundClientName","boundProviderName","consumerKind","permissionId","totalExecutions","failedExecutions","pauseReason","activatedAt","revokedAt","createdAt"]},"ListAgentSessionsResponse":{"type":"object","properties":{"agentSessions":{"type":"array","items":{"$ref":"#/components/schemas/AgentSession"}}},"required":["agentSessions"]},"AgentSessionExecution":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"amount":{"type":"string"},"currency":{"type":"string"},"transactionOpHash":{"type":["string","null"]},"transactionHash":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","status","amount","currency","transactionOpHash","transactionHash","createdAt"]},"GetAgentSessionResponse":{"allOf":[{"$ref":"#/components/schemas/AgentSession"},{"type":"object","properties":{"executions":{"type":"array","items":{"$ref":"#/components/schemas/AgentSessionExecution"}}},"required":["executions"]}]},"PauseAgentSessionResponse":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PAUSED"]}},"required":["sessionId","status"]},"ResumeAgentSessionResponse":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["ACTIVE"]}},"required":["sessionId","status"]},"RevokeAgentSessionResponse":{"type":"object","properties":{"sessionId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["REVOKED"]}},"required":["sessionId","status"]},"NotificationsPage":{"type":"object","properties":{"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"deepLink":{"type":["string","null"]},"metadata":{},"isRead":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","type","deepLink","isRead","createdAt"]}},"nextCursor":{"type":"string"}},"required":["notifications"]},"UnreadNotificationCount":{"type":"object","properties":{"count":{"type":"number"}},"required":["count"]},"MonedaMessage":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"body":{"type":"string"},"tone":{"type":"string","enum":["info","success","warning","error","neutral"]},"label":{"type":["string","null"]},"cta":{"type":["object","null"],"properties":{"label":{"type":"string"},"url":{"type":"string"}},"required":["label","url"]},"attachments":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"name":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"integer"}},"required":["index","name","mimeType","size"]}},"reply":{"type":["object","null"],"properties":{"attachment":{"type":"string","enum":["none","optional","required"]},"prompt":{"type":"string","description":"What Moneda asked for in this inquiry, e.g. \"Explain where the funds came from\"."}},"required":["attachment"]},"repliedAt":{"type":["string","null"]},"isRead":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","title","body","tone","label","cta","attachments","reply","repliedAt","isRead","createdAt"]},"MonedaMessageAttachmentUrl":{"type":"object","properties":{"url":{"type":"string"},"name":{"type":"string"},"mimeType":{"type":"string"}},"required":["url","name","mimeType"]},"MessageReply":{"type":"object","properties":{"id":{"type":"string"},"notificationId":{"type":"string"},"messageTitle":{"type":"string"},"body":{"type":"string"},"attachment":{"type":["object","null"],"properties":{"name":{"type":"string"},"mimeType":{"type":"string"},"size":{"type":"number"}},"required":["name","mimeType","size"]},"createdAt":{"type":"string"},"undoableUntil":{"type":["string","null"],"description":"While set, the reply can still be undone (DELETE /v1/notifications/replies/{id}); staff see it only after this time."}},"required":["id","notificationId","messageTitle","body","attachment","createdAt","undoableUntil"]},"MessageRepliesPage":{"type":"object","properties":{"replies":{"type":"array","items":{"$ref":"#/components/schemas/MessageReply"}},"nextCursor":{"type":"string"}},"required":["replies"]},"MessageReplyUploadSlot":{"type":"object","properties":{"uploadKey":{"type":"string"},"uploadUrl":{"type":"string"},"expiresAt":{"type":"string"},"curlCommand":{"type":"string"}},"required":["uploadKey","uploadUrl","expiresAt","curlCommand"]},"RequestMessageReplyUploadBody":{"type":"object","properties":{"mimeType":{"type":"string","enum":["application/pdf","image/jpeg","image/png"]},"fileSizeBytes":{"type":"integer","exclusiveMinimum":0}},"required":["mimeType","fileSizeBytes"]},"ReplyToMonedaMessageBody":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":600},"uploadKey":{"type":"string"},"fileName":{"type":"string","maxLength":200}},"required":["body"]},"UndoMessageReplyResult":{"type":"object","properties":{"notificationId":{"type":"string"}},"required":["notificationId"]},"RecoveryEmailsList":{"type":"object","properties":{"recoveryEmails":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"weight":{"type":"number"},"status":{"type":"string"},"invitedAt":{"type":"string"},"acceptedAt":{"type":["string","null"]},"declinedAt":{"type":["string","null"]},"removedAt":{"type":["string","null"]},"notes":{"type":["string","null"]}},"required":["id","email","weight","status","invitedAt","acceptedAt","declinedAt","removedAt","notes"]}},"count":{"type":"number"},"message":{"type":"string"}},"required":["recoveryEmails","count"]},"RecoveryEmailsListLite":{"type":"object","properties":{"recoveryEmails":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"weight":{"type":"number"},"status":{"type":"string"},"at":{"type":"string","description":"The timestamp matching the current status"}},"required":["id","email","weight","status","at"]},"description":"Omitted when there are none"},"count":{"type":"number"},"message":{"type":"string"}},"required":["count"]},"RecoveryEmailDetail":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"weight":{"type":"number"},"status":{"type":"string"},"invitedAt":{"type":"string"},"acceptedAt":{"type":["string","null"]},"declinedAt":{"type":["string","null"]},"removedAt":{"type":["string","null"]},"notes":{"type":["string","null"]},"accountAddress":{"type":"string"}},"required":["id","email","weight","status","invitedAt","acceptedAt","declinedAt","removedAt","notes","accountAddress"]},"RecoveryEmailDetailLite":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"weight":{"type":"number"},"status":{"type":"string"},"at":{"type":"string","description":"The timestamp matching the current status"},"notes":{"type":["string","null"]}},"required":["id","email","weight","status","at"]},"RecoveryConfig":{"type":"object","properties":{"accountAddress":{"type":"string"},"recoveryEmails":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"weight":{"type":"number"},"acceptedAt":{"type":["string","null"]}},"required":["id","email","weight","acceptedAt"]}},"totalWeight":{"type":"number"},"threshold":{"type":"number"}},"required":["accountAddress","recoveryEmails","totalWeight","threshold"]},"RecoveryConfigLite":{"type":"object","properties":{"fullySet":{"type":"boolean","description":"True when totalWeight reaches a non-zero threshold"},"totalWeight":{"type":"number"},"threshold":{"type":"number"},"recoveryEmails":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"weight":{"type":"number"}},"required":["email","weight"]},"description":"Accepted recovery emails. Omitted when there are none"}},"required":["fullySet","totalWeight","threshold"]},"RecoveryStatusLite":{"type":"object","properties":{"hasActiveRecovery":{"type":"boolean"},"recovery":{"type":"object","properties":{"status":{"type":"string"},"approvedCount":{"type":"number"},"totalRecoveryEmails":{"type":"number"},"approvedWeight":{"type":"number"},"threshold":{"type":"number"},"isExecutable":{"type":"boolean"},"timeUntilExecutable":{"type":"number"},"pending":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"weight":{"type":"number"}},"required":["email","weight"]}}},"required":["status","approvedCount","totalRecoveryEmails","approvedWeight","threshold","isExecutable"],"description":"Present only when a recovery is in flight"}},"required":["hasActiveRecovery"]},"ReceiptSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"transactionId":{"type":["string","null"],"format":"uuid"},"status":{"type":"string"},"fileName":{"type":"string"},"mimeType":{"type":"string"},"fileSizeBytes":{"type":"number"},"isReceipt":{"type":"boolean"},"needsReview":{"type":"boolean"},"documentType":{"type":["string","null"]},"extractionMethod":{"type":["string","null"]},"extractionModel":{"type":["string","null"]},"ocrConfidence":{"type":["number","null"]},"totalAmount":{"type":["string","null"]},"currency":{"type":["string","null"]},"merchantName":{"type":["string","null"]},"receiptDate":{"type":["string","null"]},"captureSource":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","transactionId","status","fileName","mimeType","fileSizeBytes","isReceipt","needsReview","documentType","extractionMethod","extractionModel","ocrConfidence","totalAmount","currency","merchantName","receiptDate","captureSource","createdAt"]},"ListReceiptsResponse":{"type":"object","properties":{"receipts":{"type":"array","items":{"$ref":"#/components/schemas/ReceiptSummary"}},"total":{"type":"number"}},"required":["receipts","total"]},"SearchLineItemsResponse":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"description":{"type":"string"},"quantity":{"type":["string","null"]},"totalPrice":{"type":"string"},"currency":{"type":"string"},"category":{"type":["string","null"]},"receiptDate":{"type":["string","null"]},"merchantName":{"type":["string","null"]}},"required":["receiptId","description","quantity","totalPrice","currency","category","receiptDate","merchantName"]}},"totalCount":{"type":"number"},"totalSpend":{"type":"object","additionalProperties":{"type":"string"}}},"required":["items","totalCount","totalSpend"]},"ReceiptDetail":{"allOf":[{"$ref":"#/components/schemas/ReceiptSummary"},{"type":"object","properties":{"s3Key":{"type":"string"},"fileHash":{"type":"string"},"validationError":{"type":["string","null"]},"reviewReasons":{"type":"array","items":{"type":"string"}},"subtotal":{"type":["string","null"]},"taxAmount":{"type":["string","null"]},"taxRate":{"type":["string","null"]},"tipAmount":{"type":["string","null"]},"suggestedTransactionId":{"type":["string","null"],"format":"uuid"},"suggestedMatchScore":{"type":["number","null"]},"hasBewirtungsbeleg":{"type":"boolean"},"lineItemsCount":{"type":"number"},"downloadUrl":{"type":["string","null"],"format":"uri"},"thumbnailUrl":{"type":["string","null"],"format":"uri"}},"required":["s3Key","fileHash","validationError","reviewReasons","subtotal","taxAmount","taxRate","tipAmount","suggestedTransactionId","suggestedMatchScore","hasBewirtungsbeleg","lineItemsCount","downloadUrl","thumbnailUrl"]}]},"ReceiptLineItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"receiptId":{"type":"string","format":"uuid"},"description":{"type":"string"},"orderIndex":{"type":"number"},"quantity":{"type":["string","null"]},"unitPrice":{"type":["string","null"]},"totalPrice":{"type":"string"},"taxRate":{"type":["string","null"]},"currency":{"type":"string"},"category":{"type":["string","null"]},"normalizedDescription":{"type":["string","null"]},"imageUrl":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","receiptId","description","orderIndex","quantity","unitPrice","totalPrice","taxRate","currency","category","normalizedDescription","imageUrl","createdAt"]},"UploadReceiptResponse":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"billId":{"type":["string","null"],"format":"uuid","description":"Set when the LLM classified the doc as an invoice and the Bill mirror was written. AP-queue flows thread this id through `bill.pay` / `bill.linkPayment`. Confusing it with `receiptId` will 404 in the bill table."},"deduplicated":{"type":"boolean","description":"True when the same (userId, fileHash) already exists. The existing row's ids are returned; no new processing happened."},"matched":{"type":"boolean"},"suggested":{"type":"boolean"},"transactionId":{"type":["string","null"],"format":"uuid"},"lineItemsCount":{"type":"number"},"warning":{"type":"string"}},"required":["receiptId","billId","deduplicated","matched","suggested","transactionId","lineItemsCount"]},"UploadReceiptBody":{"type":"object","properties":{"fileBase64":{"type":"string","minLength":1,"description":"File payload as base64-encoded string (PDF / JPEG / PNG)."},"fileName":{"type":"string","minLength":1,"description":"Original file name — surfaces in the UI and the receipt's `fileName` column."},"mimeType":{"type":"string","enum":["application/pdf","image/jpeg","image/png"],"description":"MIME type. Only PDF / JPEG / PNG are accepted; other types return 422."},"captureSource":{"type":"string","enum":["MANUAL_UPLOAD","EMAIL_FORWARD","GMAIL_AUTO_SCAN","CAMERA_PROMPT","CALENDAR_PROMPT","OTHER"],"description":"Provenance for the audit trail. Defaults to MANUAL_UPLOAD for direct user uploads."},"sourceMetadata":{"type":"object","additionalProperties":{},"description":"Opaque audit payload — Resend email id, Gmail message id, DKIM status, etc. Persisted on the row's sourceMetadata JSON column for forensics."}},"required":["fileBase64","fileName","mimeType"]},"RequestReceiptUploadResponse":{"type":"object","properties":{"uploadKey":{"type":"string"},"uploadUrl":{"type":"string","format":"uri"},"expiresAt":{"type":"string"},"maxSizeBytes":{"type":"integer","exclusiveMinimum":0},"curlCommand":{"type":"string"}},"required":["uploadKey","uploadUrl","expiresAt","maxSizeBytes","curlCommand"]},"RequestReceiptUploadBody":{"type":"object","properties":{"fileName":{"type":"string","minLength":1,"maxLength":256},"mimeType":{"type":"string","enum":["application/pdf","image/jpeg","image/png"]},"fileSizeBytes":{"type":"integer","exclusiveMinimum":0}},"required":["fileName","mimeType","fileSizeBytes"]},"FinalizeReceiptUploadBody":{"type":"object","properties":{"uploadKey":{"type":"string"},"captureSource":{"type":"string","enum":["MANUAL_UPLOAD","EMAIL_FORWARD","GMAIL_AUTO_SCAN","CAMERA_PROMPT","CALENDAR_PROMPT","OTHER"]}},"required":["uploadKey"]},"ImportReceiptFromUrlBody":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"fileName":{"type":"string","maxLength":256},"mimeType":{"type":"string","enum":["application/pdf","image/jpeg","image/png"]},"captureSource":{"type":"string","enum":["MANUAL_UPLOAD","EMAIL_FORWARD","GMAIL_AUTO_SCAN","CAMERA_PROMPT","CALENDAR_PROMPT","OTHER"]}},"required":["url"]},"ReparseReceiptResponse":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["UPLOADING","PROCESSING","VALID","INVALID","DELETED"]},"ocrConfidence":{"type":["number","null"]},"documentType":{"type":["string","null"]},"extractionMethod":{"type":["string","null"]},"extractionModel":{"type":["string","null"]},"needsReview":{"type":"boolean"},"validationError":{"type":["string","null"]},"lineItemsCount":{"type":"integer"},"updatedAt":{"type":"string"}},"required":["receiptId","status","ocrConfidence","documentType","extractionMethod","extractionModel","needsReview","validationError","lineItemsCount","updatedAt"]},"BewirtungsbelegSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"receiptId":{"type":"string","format":"uuid"},"transactionId":{"type":["string","null"],"format":"uuid"},"date":{"type":"string"},"location":{"type":"string"},"occasion":{"type":"string"},"totalAmount":{"type":["string","null"]},"tipAmount":{"type":["string","null"]},"subtotal":{"type":["string","null"]},"attendeeCount":{"type":"number"},"isInternalEvent":{"type":"boolean"},"deductibleAmount":{"type":["string","null"]},"nonDeductibleAmount":{"type":["string","null"]},"isSigned":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","receiptId","transactionId","date","location","occasion","totalAmount","tipAmount","subtotal","attendeeCount","isInternalEvent","deductibleAmount","nonDeductibleAmount","isSigned","createdAt"]},"BewirtungsbelegDetail":{"allOf":[{"$ref":"#/components/schemas/BewirtungsbelegSummary"},{"type":"object","properties":{"hostCompanyName":{"type":["string","null"]},"hostCompanyAddress":{"type":["string","null"]},"restaurantTaxId":{"type":["string","null"]},"signedAt":{"type":["string","null"]},"attendees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"relationship":{"type":"string"}},"required":["id","name","relationship"]}}},"required":["hostCompanyName","hostCompanyAddress","restaurantTaxId","signedAt","attendees"]}]},"EigenbelegSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"transactionId":{"type":"string","format":"uuid"},"date":{"type":"string"},"recipientName":{"type":"string"},"description":{"type":"string"},"amount":{"type":"string"},"reason":{"type":"string"},"isSigned":{"type":"boolean"},"createdAt":{"type":"string"}},"required":["id","transactionId","date","recipientName","description","amount","reason","isSigned","createdAt"]},"EigenbelegDetail":{"allOf":[{"$ref":"#/components/schemas/EigenbelegSummary"},{"type":"object","properties":{"recipientAddress":{"type":"string"},"signedAt":{"type":["string","null"]}},"required":["recipientAddress","signedAt"]}]},"BillSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"fileName":{"type":"string"},"mimeType":{"type":"string"},"paymentStatus":{"type":"string","enum":["PENDING","PAID_VIA_MONEDA","PAID_EXTERNALLY","DISMISSED"]},"paidByTransactionId":{"type":["string","null"],"format":"uuid"},"paidExternallyAt":{"type":["string","null"]},"paidExternallyNote":{"type":["string","null"]},"vendorVerification":{},"needsReview":{"type":"boolean"},"recipientMerchantId":{"type":["string","null"],"format":"uuid"},"dueDate":{"type":["string","null"]},"merchantName":{"type":["string","null"]},"totalAmount":{"type":["number","null"]},"currency":{"type":["string","null"]},"invoiceNumber":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","fileName","mimeType","paymentStatus","paidByTransactionId","paidExternallyAt","paidExternallyNote","needsReview","recipientMerchantId","dueDate","merchantName","totalAmount","currency","invoiceNumber","createdAt","updatedAt"]},"ListBillsResponse":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/BillSummary"}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"]},"BillMerchant":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"legalName":{"type":["string","null"]},"displayName":{"type":["string","null"]},"taxId":{"type":["string","null"]},"primaryDomain":{"type":["string","null"]},"countryOfIncorporation":{"type":["string","null"]},"email":{"type":["string","null"]},"logoUrl":{"type":["string","null"]},"mccCode":{"type":["string","null"]}},"required":["id","legalName","displayName","taxId","primaryDomain","countryOfIncorporation","email","logoUrl","mccCode"]},"BillDetail":{"allOf":[{"$ref":"#/components/schemas/BillSummary"},{"type":"object","properties":{"fileSizeBytes":{"type":"number"},"recipientMerchant":{"$ref":"#/components/schemas/BillMerchant"},"merchantPinned":{"type":"boolean"},"downloadUrl":{"type":["string","null"],"format":"uri"},"ocrExtractedData":{},"merchantTaxId":{"type":["string","null"]},"merchantAddress":{"type":["string","null"]},"date":{"type":["string","null"]},"vendorIban":{"type":["string","null"]},"vendorBic":{"type":["string","null"]},"vendorRoutingNumber":{"type":["string","null"]},"vendorAccountNumber":{"type":["string","null"]}},"required":["fileSizeBytes","recipientMerchant","merchantPinned","downloadUrl","merchantTaxId","merchantAddress","date","vendorIban","vendorBic","vendorRoutingNumber","vendorAccountNumber"]}]},"UsageCreditPurchaseResult":{"type":"object","properties":{"granted":{"type":"boolean","description":"False when this charge was already redeemed (idempotent replay)."},"txHash":{"type":"string"},"paidAmount":{"type":"number"},"paidCurrency":{"type":"string"},"budgetUsd":{"type":"string","description":"AI budget granted (USD)."},"creditBalanceUsd":{"type":"string"}},"required":["granted","txHash","paidAmount","paidCurrency","budgetUsd","creditBalanceUsd"]},"UsageCreditMonthlyLimitResult":{"type":"object","properties":{"monthlySpendLimitUsd":{"type":["string","null"],"description":"The persisted monthly limit (USD), or null when unlimited."},"unlimited":{"type":"boolean"}},"required":["monthlySpendLimitUsd","unlimited"]},"PlatformOrganization":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"kybStatus":{"type":"string","enum":["UNVERIFIED","PENDING","VERIFIED","REJECTED"]},"billingMode":{"type":"string","enum":["PREPAID","INVOICED"]},"createdAt":{"type":"string"}},"required":["id","name","slug","kybStatus","billingMode","createdAt"]},"PlatformOrganizationWithRole":{"allOf":[{"$ref":"#/components/schemas/PlatformOrganization"},{"type":"object","properties":{"role":{"type":"string","enum":["OWNER","ADMIN","DEVELOPER","BILLING","COMPLIANCE","MEMBER"],"example":"DEVELOPER"}},"required":["role"]}]},"PlatformWorkspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","name","slug","createdAt"]},"PlatformMember":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string","enum":["OWNER","ADMIN","DEVELOPER","BILLING","COMPLIANCE","MEMBER"],"example":"DEVELOPER"},"status":{"type":"string","enum":["ACTIVE","INVITED","REMOVED"]},"createdAt":{"type":"string"}},"required":["userId","role","status","createdAt"]},"PlatformCreatedInvite":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["OWNER","ADMIN","DEVELOPER","BILLING","COMPLIANCE","MEMBER"],"example":"DEVELOPER"},"token":{"type":"string","description":"Raw invite token — shown only once."},"expiresAt":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","email","role","token","expiresAt","createdAt"]},"PlatformInvite":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["OWNER","ADMIN","DEVELOPER","BILLING","COMPLIANCE","MEMBER"],"example":"DEVELOPER"},"acceptedAt":{"type":["string","null"]},"expiresAt":{"type":"string"},"createdAt":{"type":"string"},"status":{"type":"string","enum":["PENDING","ACCEPTED","EXPIRED"]}},"required":["id","email","role","acceptedAt","expiresAt","createdAt","status"]},"PlatformIssuedKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"key":{"type":"string","description":"Raw secret — shown only once."},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"workspaceId":{"type":"string"},"sandbox":{"type":"boolean","description":"true for a sandbox (mnd_test_) key."},"expiresAt":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","name","key","prefix","scopes","workspaceId","sandbox","expiresAt","createdAt"]},"PlatformKeySummary":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"sandbox":{"type":"boolean"},"serviceAccountId":{"type":["string","null"]},"lastUsedAt":{"type":["string","null"]},"expiresAt":{"type":["string","null"]},"revokedAt":{"type":["string","null"]},"createdAt":{"type":"string"},"isActive":{"type":"boolean"}},"required":["id","name","prefix","scopes","sandbox","serviceAccountId","lastUsedAt","expiresAt","revokedAt","createdAt","isActive"]},"PlatformUsageLimit":{"type":"object","properties":{"id":{"type":"string"},"scope":{"type":"string","enum":["ORG","WORKSPACE","KEY","GLOBAL"]},"targetId":{"type":"string"},"limitType":{"type":"string","enum":["SPEND_HARD","SPEND_SOFT","RATE_RPM","RATE_TPM","RATE_RPD"],"example":"SPEND_HARD"},"value":{"type":"number"},"windowSeconds":{"type":["number","null"]},"notifyThresholdPct":{"type":["number","null"]},"enabled":{"type":"boolean"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","scope","targetId","limitType","value","windowSeconds","notifyThresholdPct","enabled","createdAt","updatedAt"]},"PlatformWebhookEndpoint":{"type":"object","properties":{"id":{"type":"string"},"workspaceId":{"type":"string"},"url":{"type":"string"},"description":{"type":["string","null"]},"enabledEvents":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["ENABLED","DISABLED"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","workspaceId","url","description","enabledEvents","status","createdAt","updatedAt"]},"PlatformCreatedWebhook":{"allOf":[{"$ref":"#/components/schemas/PlatformWebhookEndpoint"},{"type":"object","properties":{"signingSecret":{"type":"string","description":"Signing secret — shown only once."}},"required":["signingSecret"]}]},"PlatformAuditLogPage":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"actorUserId":{"type":["string","null"]},"actorApiKeyId":{"type":["string","null"]},"action":{"type":"string"},"targetType":{"type":"string"},"targetId":{"type":["string","null"]},"metadata":{"description":"Arbitrary JSON recorded with the action; always present, may be null."},"ipAddress":{"type":["string","null"]},"occurredAt":{"type":"string"}},"required":["id","actorUserId","actorApiKeyId","action","targetType","targetId","ipAddress","occurredAt"]}},"nextBefore":{"type":["string","null"]}},"required":["items","nextBefore"]},"PlatformUsageRecordPage":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"surface":{"type":"string"},"workspaceId":{"type":["string","null"]},"apiKeyId":{"type":["string","null"]},"tool":{"type":["string","null"]},"endpoint":{"type":["string","null"]},"method":{"type":["string","null"]},"tokensIn":{"type":["number","null"]},"tokensOut":{"type":["number","null"]},"quantity":{"type":"number"},"costUsd":{"type":"string"},"upstreamCostUsd":{"type":["string","null"]},"creditSource":{"type":"string"},"status":{"type":"number"},"latencyMs":{"type":["number","null"]},"occurredAt":{"type":"string"}},"required":["id","surface","workspaceId","apiKeyId","tool","endpoint","method","tokensIn","tokensOut","quantity","costUsd","upstreamCostUsd","creditSource","status","latencyMs","occurredAt"]}},"nextBefore":{"type":["string","null"]}},"required":["items","nextBefore"]},"PlatformPricedTool":{"type":"object","properties":{"key":{"type":"string"},"displayName":{"type":"string"},"category":{"type":["string","null"]},"unit":{"type":"string","enum":["PER_CALL","PER_1K_TOKENS","PER_GB_DAY"]},"unitPriceUsd":{"type":"string"},"markupMultiplier":{"type":"string"},"pricePerUnitUsd":{"type":"string"},"scope":{"type":["string","null"]},"organizationId":{"type":["string","null"]}},"required":["key","displayName","category","unit","unitPriceUsd","markupMultiplier","pricePerUnitUsd","scope","organizationId"]},"PlatformLegalDocRef":{"type":"object","properties":{"kind":{"type":"string"},"url":{"type":"string","format":"uri"},"version":{"type":"string"},"signedAt":{"type":"string"}},"required":["kind","url"]},"PlatformOrganizationContract":{"type":["object","null"],"properties":{"organizationId":{"type":"string"},"status":{"type":"string","enum":["DRAFT","ACTIVE","EXPIRED","TERMINATED"]},"committedAmountUsd":{"type":["string","null"]},"termStart":{"type":["string","null"]},"termEnd":{"type":["string","null"]},"autoRenew":{"type":"boolean"},"slaTier":{"type":["string","null"]},"dedicatedUpstreamCapacity":{"type":"boolean"},"legalDocRefs":{"type":"array","items":{"$ref":"#/components/schemas/PlatformLegalDocRef"}},"billingMode":{"type":"string","enum":["PREPAID","INVOICED"]},"dataResidency":{"type":["string","null"]},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["organizationId","status","committedAmountUsd","termStart","termEnd","autoRenew","slaTier","dedicatedUpstreamCapacity","legalDocRefs","billingMode","dataResidency","createdAt","updatedAt"]}}},"paths":{"/v1/health":{"get":{"tags":["Health"],"operationId":"health","summary":"Check API health","description":"Public endpoint: no authentication is required. Reports service status (`ok` or `degraded`), database connectivity, uptime, version and the deployed commit.","responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[]}},"/v1/sandbox/keys":{"post":{"tags":["Sandbox"],"operationId":"create_sandbox_key","security":[],"summary":"Get a sandbox API key (no account, no authentication)","description":"Sandbox host only. Returns a key valid for 24 hours that works on every endpoint of the sandbox host. Sandbox responses are mock data generated from this OpenAPI document: there is no account, no balance and no funds behind them, and nothing is ever executed. Health, the OpenAPI document and the knowledge base return real content. On the production host this endpoint answers 404.","responses":{"201":{"description":"Sandbox key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxKey"}}}},"404":{"description":"Not the sandbox host","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/balances":{"get":{"tags":["Balances"],"operationId":"get_balance","summary":"Get account balances","description":"Returns the balance of each of the authenticated user's currency accounts, optionally filtered to one `currency`. Balances are read live from the user's self-custodial smart account; if the live read fails the latest stored snapshot is returned instead, and `source` says which one you got. `?view=lite` trims snapshot metadata and renames accountName to type.","security":[{"OAuth2":["read:balances"]},{"ApiKey":["read:balances"]}],"parameters":[{"schema":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"],"example":"USD"},"required":false,"name":"currency","in":"query"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Account balances. `?view=lite` trims snapshot metadata and renames accountName → type.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/BalanceResponse"},{"$ref":"#/components/schemas/BalanceResponseLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The user has no active smart account (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/transactions":{"get":{"tags":["Transactions"],"operationId":"get_transactions","summary":"List transactions","description":"Returns the authenticated user's transaction history, newest first by default, with filters for currency, type, type group, category, status, direction, counterparty, date range, amount, card, and free-text search over note and reference. Only COMPLETED and COMPLETED_DELAYED rows are returned unless `status` is set (`all` includes every status). Paginate with `limit` (default 20, max 100) and `offset`, or with `cursor`, in which case `nextCursor` is returned while more rows exist. `?view=lite` returns a compact row shape and never includes `nextCursor`.","security":[{"OAuth2":["read:transactions"]},{"ApiKey":["read:transactions"]}],"parameters":[{"schema":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]},"required":false,"name":"currency","in":"query"},{"schema":{"type":"string"},"required":false,"name":"type","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string"},"required":false,"name":"startDate","in":"query"},{"schema":{"type":"string"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"string"},"required":false,"name":"category","in":"query"},{"schema":{"type":"string"},"required":false,"name":"counterparty","in":"query"},{"schema":{"type":"string"},"required":false,"name":"minAmount","in":"query"},{"schema":{"type":"string"},"required":false,"name":"maxAmount","in":"query"},{"schema":{"type":"string"},"required":false,"name":"year","in":"query"},{"schema":{"type":"string"},"required":false,"name":"typeGroup","in":"query"},{"schema":{"type":"string","enum":["newest","oldest","amount_desc","amount_asc"]},"required":false,"name":"sort","in":"query"},{"schema":{"type":"string","enum":["COMPLETED","COMPLETED_DELAYED","PENDING","FAILED","all"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"hasNote","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"hasCategory","in":"query"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"hasReceipt","in":"query"},{"schema":{"type":"string"},"required":false,"name":"transactionHash","in":"query"},{"schema":{"type":"string","enum":["incoming","outgoing","exchange"]},"required":false,"name":"direction","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"cardId","in":"query"},{"schema":{"type":"string"},"required":false,"name":"card","in":"query"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Transaction list","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TransactionListResponse"},{"$ref":"#/components/schemas/TransactionListResponseLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/transactions/{hash}":{"get":{"tags":["Transactions"],"operationId":"get_transaction","summary":"Get a transaction by hash","description":"Returns a single transaction owned by the authenticated user, looked up by transaction hash (with or without the 0x prefix, case-insensitive). Unlike the list endpoint it returns the transaction in any status, including PENDING and FAILED. Responds 404 when the hash does not exist or belongs to another user. `?view=lite` returns the compact shape.","security":[{"OAuth2":["read:transactions"]},{"ApiKey":["read:transactions"]}],"parameters":[{"schema":{"type":"string","example":"0x73e605943add85b6bd5d8c408b5604fc39dcf298"},"required":true,"name":"hash","in":"path"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Transaction. `?view=lite` returns the compact shape (hash, direction, amount, currency, status, date, counterparty). Omit for the full row (typeGroup, category, note, reference, raw counterparty routing).","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"transactionHash":{"type":"string"},"typeGroup":{"type":"string"},"type":{"type":"string"},"category":{"type":"string"},"note":{"type":"string"},"direction":{"type":"string","enum":["outgoing","incoming","exchange"]},"amount":{"type":"string"},"currency":{"type":"string"},"status":{"type":"string"},"reference":{"type":"string"},"counterparty":{"type":"object","properties":{"type":{"type":"string","enum":["self","user","provider","bank","wallet","unknown"]},"name":{"type":"string"},"isExternalUser":{"type":"boolean","description":"`user` counterparties only: false for a Moneda account, true for an external contact"},"bank":{"type":"string","description":"Bank counterparties only: the institution name"},"username":{"type":"string"},"address":{"type":"string"},"iban":{"type":"string"},"account":{"type":"string"},"routing":{"type":"string"}},"required":["type"]},"date":{"type":"string"},"exchanged":{"type":"string"},"exchangedCurrency":{"type":"string"},"received":{"type":"string"},"receivedCurrency":{"type":"string"},"asset":{"type":"object","properties":{"symbol":{"type":"string"}},"required":["symbol"],"description":"The instrument a trade was in — the crypto asset key or the dShare ticker. Present only on CRYPTO_* and DSHARE_* rows."},"trade":{"type":"object","properties":{"venue":{"type":"string","enum":["crypto","stock"]},"status":{"type":"string"},"soldAssetAmount":{"type":"string"},"minReceivedAmount":{"type":"string"},"receivedAmount":{"type":"string"},"quotedAssetAmount":{"type":"string"},"filledAssetAmount":{"type":"string"},"quotedPrice":{"type":"string"},"slippageBps":{"type":"number"},"feeAmount":{"type":"string"},"sourceFeeAmount":{"type":"string"},"monedaFeeAmount":{"type":"string"},"monedaFeeCurrency":{"type":"string"},"orderType":{"type":"string"},"orderStatus":{"type":"string"},"requestedQuantity":{"type":"string"},"limitPrice":{"type":"string"},"filledQuantity":{"type":"string"},"avgFillPrice":{"type":"string"},"submittedAt":{"type":"string"},"filledAt":{"type":"string"},"failedAt":{"type":"string"},"failureReason":{"type":"string"}},"description":"Fills, fees and any failure of a trade. Present only on trade rows, alongside `asset`."},"card":{"type":"object","properties":{"cardId":{"type":"string","format":"uuid"},"last4":{"type":"string"},"merchantName":{"type":"string"},"merchantCategoryCode":{"type":"string"},"merchantCountryCode":{"type":"string"},"declinedReason":{"type":"string"}},"required":["cardId"]},"merchant":{"type":"object","properties":{"name":{"type":"string"},"logoUrl":{"type":"string"}},"required":["name"]},"signer":{"type":"object","properties":{"label":{"type":"string","description":"User label, or the derived provider name"},"kind":{"type":"string","enum":["primary","backup"]}},"required":["label","kind"]},"refundOf":{"type":["object","null"],"properties":{"id":{"type":"string"},"transactionHash":{"type":["string","null"]}},"required":["id","transactionHash"],"description":"The outbound transaction this row refunds (e.g. LiFi change, bridge refund); null otherwise."},"refunds":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"transactionHash":{"type":["string","null"]},"amount":{"type":"string"},"currency":{"type":"string"},"createdAt":{"type":"string"}},"required":["id","transactionHash","amount","currency","createdAt"]},"description":"Completed refunds of this transaction, oldest first."}},"required":["typeGroup","type","direction","amount","currency","status","date"]},{"$ref":"#/components/schemas/TransactionLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Transaction not found or not owned by this user (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/transactions/{hash}/category":{"patch":{"tags":["Transactions"],"operationId":"update_transaction_category","summary":"Set a transaction's spending category","description":"Assigns a spending category to one of the user's transactions, identified by transaction hash, replacing any category already set. The category must be one of the supported category ids, otherwise the request fails with a validation error. Fails with not found when the hash does not belong to the user.","security":[{"OAuth2":["write:transactions"]},{"ApiKey":["write:transactions"]}],"parameters":[{"schema":{"type":"string","example":"0x73e605943add85b6bd5d8c408b5604fc39dcf298"},"required":true,"name":"hash","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string"}},"required":["category"]}}}},"responses":{"200":{"description":"Category updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCategoryResponse"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No transaction with this hash belongs to the user (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Unknown category id (VALIDATION_ERROR). The message lists the valid ids","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/transactions/{hash}/note":{"patch":{"tags":["Transactions"],"operationId":"add_transaction_note","summary":"Add or update a transaction note","description":"Adds a note to one of the user's transactions, identified by transaction hash, replacing any existing note. The note is trimmed and must be between 1 and 140 characters. Fails with not found when the hash does not belong to the user.","security":[{"OAuth2":["write:transactions"]},{"ApiKey":["write:transactions"]}],"parameters":[{"schema":{"type":"string","example":"0x73e605943add85b6bd5d8c408b5604fc39dcf298"},"required":true,"name":"hash","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string"}},"required":["note"]}}}},"responses":{"200":{"description":"Note updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNoteResponse"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No transaction with this hash belongs to the user (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Note is empty after trimming or longer than 140 characters (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/transactions/batch/categories":{"post":{"tags":["Transactions"],"operationId":"batch_update_transaction_categories","summary":"Set categories on up to 25 transactions","description":"Assigns spending categories to between 1 and 25 of the user's transactions in one request, each identified by transaction hash. Each item is validated and applied on its own: an unknown category or a hash that does not belong to the user fails only that item, and `results` reports the outcome per transaction alongside the `succeeded` and `failed` totals.","security":[{"OAuth2":["write:transactions"]},{"ApiKey":["write:transactions"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"updates":{"type":"array","items":{"type":"object","properties":{"transactionHash":{"type":"string"},"category":{"type":"string"}},"required":["transactionHash","category"]},"maxItems":25}},"required":["updates"]}}}},"responses":{"200":{"description":"Categories updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchCategoryResponse"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"`updates` is empty (VALIDATION_ERROR). Per-item problems are reported in `results`, not as an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/transactions/batch/notes":{"post":{"tags":["Transactions"],"operationId":"batch_add_transaction_notes","summary":"Add notes to up to 25 transactions","description":"Adds or replaces notes on between 1 and 25 of the user's transactions in one request, each identified by transaction hash. Each note must be 1 to 140 characters. Each item is applied on its own: a hash that does not belong to the user fails only that item, and `results` reports the outcome per transaction alongside the `succeeded` and `failed` totals.","security":[{"OAuth2":["write:transactions"]},{"ApiKey":["write:transactions"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"updates":{"type":"array","items":{"type":"object","properties":{"transactionHash":{"type":"string"},"note":{"type":"string","minLength":1,"maxLength":140}},"required":["transactionHash","note"]},"maxItems":25}},"required":["updates"]}}}},"responses":{"200":{"description":"Notes updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchNoteResponse"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"`updates` is empty (VALIDATION_ERROR). Per-item problems are reported in `results`, not as an error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/spending/by-type-group":{"get":{"tags":["Spending"],"operationId":"get_spending_by_type_group","summary":"Get spending grouped by transaction type group","description":"Returns the user's spending over a rolling `period` (7d, 30d or 90d; default 30d), grouped by transaction type group, with a count and total per group plus the overall total and transaction count. It counts the user's completed outgoing transactions, net of completed refunds. Pass `currency` to scope the totals to one currency; without it amounts in different currencies are added together.","security":[{"OAuth2":["read:spending"]},{"ApiKey":["read:spending"]}],"parameters":[{"schema":{"type":"string","enum":["7d","30d","90d"]},"required":false,"name":"period","in":"query"},{"schema":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]},"required":false,"name":"currency","in":"query"}],"responses":{"200":{"description":"Spending grouped by transaction type group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingByTypeGroup"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/spending/daily":{"get":{"tags":["Spending"],"operationId":"get_daily_spending","summary":"Get daily spending totals","description":"Returns one spending total per day for the given `currency` (required), suitable for charting. Pass either a rolling `period` (1d, 1w, 1m, 1y; default 1m) or an absolute UTC window with `from` (inclusive) and `to` (exclusive) together, never both.","security":[{"OAuth2":["read:spending"]},{"ApiKey":["read:spending"]}],"parameters":[{"schema":{"type":"string","enum":["1d","1w","1m","1y"],"description":"Rolling time window. Mutually exclusive with `from`/`to`."},"required":false,"description":"Rolling time window. Mutually exclusive with `from`/`to`.","name":"period","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive ISO 8601 UTC start of an absolute window."},"required":false,"description":"Inclusive ISO 8601 UTC start of an absolute window.","name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Exclusive ISO 8601 UTC end of an absolute window."},"required":false,"description":"Exclusive ISO 8601 UTC end of an absolute window.","name":"to","in":"query"},{"schema":{"type":"string","enum":["USDC","EURC","CHFAU","CADD","MXNB"]},"required":true,"name":"currency","in":"query"}],"responses":{"200":{"description":"Daily spending totals","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DailySpending"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid window or filter combination (VALIDATION_ERROR): `from` without `to`, `period` together with `from`/`to`, `from` not earlier than `to`, or a range that is too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/spending/by-category":{"get":{"tags":["Spending"],"operationId":"get_spending_by_category","summary":"Get spending grouped by category","description":"Returns the user's spending for the given `currency` (required) grouped by the category assigned to each transaction, with a count and total per bucket. Transactions without a category land in an UNCATEGORIZED bucket. Pass either a rolling `period` (1d, 1w, 1m, 1y; default 1m) or an absolute UTC window with `from` (inclusive) and `to` (exclusive) together, never both.","security":[{"OAuth2":["read:spending"]},{"ApiKey":["read:spending"]}],"parameters":[{"schema":{"type":"string","enum":["1d","1w","1m","1y"],"description":"Rolling time window. Mutually exclusive with `from`/`to`."},"required":false,"description":"Rolling time window. Mutually exclusive with `from`/`to`.","name":"period","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive ISO 8601 UTC start of an absolute window."},"required":false,"description":"Inclusive ISO 8601 UTC start of an absolute window.","name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Exclusive ISO 8601 UTC end of an absolute window."},"required":false,"description":"Exclusive ISO 8601 UTC end of an absolute window.","name":"to","in":"query"},{"schema":{"type":"string","enum":["USDC","EURC","CHFAU","CADD","MXNB"]},"required":true,"name":"currency","in":"query"}],"responses":{"200":{"description":"Spending grouped by category","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingByCategory"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid window or filter combination (VALIDATION_ERROR): `from` without `to`, `period` together with `from`/`to`, `from` not earlier than `to`, or a range that is too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/spending/by-contact":{"get":{"tags":["Spending"],"operationId":"get_spending_by_contact","summary":"Get spending sent to one recipient","description":"Returns the total amount and number of transactions the user sent to a single recipient account (`recipientAccountId`, required) in the given `currency` (required). Pass either a rolling `period` (1d, 1w, 1m, 1y; default 1m) or an absolute UTC window with `from` (inclusive) and `to` (exclusive) together, never both.","security":[{"OAuth2":["read:spending"]},{"ApiKey":["read:spending"]}],"parameters":[{"schema":{"type":"string","enum":["1d","1w","1m","1y"],"description":"Rolling time window. Mutually exclusive with `from`/`to`."},"required":false,"description":"Rolling time window. Mutually exclusive with `from`/`to`.","name":"period","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive ISO 8601 UTC start of an absolute window."},"required":false,"description":"Inclusive ISO 8601 UTC start of an absolute window.","name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Exclusive ISO 8601 UTC end of an absolute window."},"required":false,"description":"Exclusive ISO 8601 UTC end of an absolute window.","name":"to","in":"query"},{"schema":{"type":"string","enum":["USDC","EURC","CHFAU","CADD","MXNB"]},"required":true,"name":"currency","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"recipientAccountId","in":"query"}],"responses":{"200":{"description":"Spending sent to a single recipient","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingByContact"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid window or filter combination (VALIDATION_ERROR): `from` without `to`, `period` together with `from`/`to`, `from` not earlier than `to`, or a range that is too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/spending/by-merchant":{"get":{"tags":["Spending"],"operationId":"get_spending_by_merchant","summary":"Get spending grouped by recipient or merchant","description":"Returns the user's spending for the given `currency` (required) grouped by recipient, with a count and total per bucket. Each bucket carries a `type`: user, bank-withdrawal, external-wallet, coinbase, wallet-connect or other. Pass either a rolling `period` (1d, 1w, 1m, 1y; default 1m) or an absolute UTC window with `from` (inclusive) and `to` (exclusive) together, never both.","security":[{"OAuth2":["read:spending"]},{"ApiKey":["read:spending"]}],"parameters":[{"schema":{"type":"string","enum":["1d","1w","1m","1y"],"description":"Rolling time window. Mutually exclusive with `from`/`to`."},"required":false,"description":"Rolling time window. Mutually exclusive with `from`/`to`.","name":"period","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive ISO 8601 UTC start of an absolute window."},"required":false,"description":"Inclusive ISO 8601 UTC start of an absolute window.","name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Exclusive ISO 8601 UTC end of an absolute window."},"required":false,"description":"Exclusive ISO 8601 UTC end of an absolute window.","name":"to","in":"query"},{"schema":{"type":"string","enum":["USDC","EURC","CHFAU","CADD","MXNB"]},"required":true,"name":"currency","in":"query"}],"responses":{"200":{"description":"Spending grouped by recipient/merchant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingByMerchant"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid window or filter combination (VALIDATION_ERROR): `from` without `to`, `period` together with `from`/`to`, `from` not earlier than `to`, or a range that is too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/spending/comparison":{"get":{"tags":["Spending"],"operationId":"get_spending_comparison","summary":"Compare spending across two time windows","description":"Returns spending totals and transaction counts for two caller-supplied windows (`currentFrom`/`currentTo` and `previousFrom`/`previousTo`, ISO 8601 UTC) in the given `currency`, plus the absolute delta and the percentage delta. All five query parameters are required. `deltaPct` can be null.","security":[{"OAuth2":["read:spending"]},{"ApiKey":["read:spending"]}],"parameters":[{"schema":{"type":"string","enum":["USDC","EURC","CHFAU","CADD","MXNB"]},"required":true,"name":"currency","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":true,"name":"currentFrom","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":true,"name":"currentTo","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":true,"name":"previousFrom","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":true,"name":"previousTo","in":"query"}],"responses":{"200":{"description":"Side-by-side spending comparison for two windows","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingComparison"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid window (VALIDATION_ERROR): a `from` that is not earlier than its `to`, or a range that is too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/spending/transactions":{"get":{"tags":["Spending"],"operationId":"get_spending_transactions","summary":"List the transactions behind a spending breakdown","description":"Lists the individual spending transactions for a window, optionally narrowed to one `currency`, one category or merchant bucket (`bucketKind` with `bucketId`, and `bucketType` for merchant buckets) or one `recipientAccountId`. Sort with `orderBy` and `direction`, and paginate with `take` (default 50, max 200) and `skip`; `hasMore` signals another page. Pass either a rolling `period` (1d, 1w, 1m, 1y; default 1m) or an absolute UTC window with `from` (inclusive) and `to` (exclusive) together, never both.","security":[{"OAuth2":["read:spending"]},{"ApiKey":["read:spending"]}],"parameters":[{"schema":{"type":"string","enum":["1d","1w","1m","1y"],"description":"Rolling time window. Mutually exclusive with `from`/`to`."},"required":false,"description":"Rolling time window. Mutually exclusive with `from`/`to`.","name":"period","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive ISO 8601 UTC start of an absolute window."},"required":false,"description":"Inclusive ISO 8601 UTC start of an absolute window.","name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Exclusive ISO 8601 UTC end of an absolute window."},"required":false,"description":"Exclusive ISO 8601 UTC end of an absolute window.","name":"to","in":"query"},{"schema":{"type":"string","enum":["USDC","EURC","CHFAU","CADD","MXNB"]},"required":false,"name":"currency","in":"query"},{"schema":{"type":"string","enum":["category","merchant"]},"required":false,"name":"bucketKind","in":"query"},{"schema":{"type":"string"},"required":false,"name":"bucketId","in":"query"},{"schema":{"type":"string","enum":["user","bank-withdrawal","external-wallet","coinbase","wallet-connect","other"]},"required":false,"name":"bucketType","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"recipientAccountId","in":"query"},{"schema":{"type":"string","enum":["createdAt","amount"]},"required":false,"name":"orderBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"]},"required":false,"name":"direction","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"take","in":"query"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"skip","in":"query"}],"responses":{"200":{"description":"Spending transactions for the requested window/bucket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingTransactions"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid window or filter combination (VALIDATION_ERROR): `from` without `to`, `period` together with `from`/`to`, `bucketKind` without `bucketId`, or a range that is too long","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/earnings/time-series":{"get":{"tags":["Earnings"],"operationId":"get_earnings_time_series","summary":"Get the earnings time series for a vault account","description":"Returns a time series of earnings and balance points for one of the user's Morpho or YO vault accounts, plus the currency, total earnings and net deposits, for charting yield over time. `accountId`, `chainRef` and `vaultProvider` are all required. Responds 404 when the account does not exist or belongs to another user.","security":[{"OAuth2":["read:earnings"]},{"ApiKey":["read:earnings"]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"chainRef","in":"query"},{"schema":{"type":"string"},"required":true,"name":"accountId","in":"query"},{"schema":{"type":"string","enum":["MORPHO","YO"]},"required":true,"name":"vaultProvider","in":"query"}],"responses":{"200":{"description":"Daily earnings/balance points for a vault account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EarningsTimeSeries"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The account has an unsupported currency or no on-chain address (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/exchange-rates":{"get":{"tags":["Exchange Rates"],"operationId":"get_exchange_rate","summary":"Get the exchange rate between two currencies","description":"Returns the most recently stored conversion rate from one supported currency to another, with the time it was last updated. `from` defaults to USD and `to` defaults to EUR. Rates are stored reference rates that are refreshed periodically and may be served from a short cache. Responds 404 when no rate is stored for the pair.","security":[{"OAuth2":["read:rates"]},{"ApiKey":["read:rates"]}],"parameters":[{"schema":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"The stored rate for the requested currency pair","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeRate"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No rate is stored for this currency pair (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/apy-rates":{"get":{"tags":["APY Rates"],"operationId":"get_apy_rates","summary":"List current earnings APY rates","description":"Returns the current APY for each earnings product, with its currency and the time the rate was last updated. Optionally filter to one `currency`; a currency with no earnings product returns an empty array. The rates are the same for every user.","security":[{"OAuth2":["read:earnings"]},{"ApiKey":["read:earnings"]}],"parameters":[{"schema":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]},"required":false,"name":"currency","in":"query"}],"responses":{"200":{"description":"APY rates","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApyRate"}}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/dinari/capabilities":{"get":{"tags":["Dinari"],"operationId":"get_dinari_capabilities","summary":"Get tokenized-equity trading capabilities","description":"Returns what the user may currently do with tokenized equities (jurisdiction, whether they can trade, order limits and fees) and, when they cannot, the reasons that block them.","security":[{"OAuth2":["read:account"]},{"ApiKey":["read:account"]}],"responses":{"200":{"description":"What the user may currently do with tokenized equities, and why not if they can't","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DinariCapabilities"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:account scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/dinari/us/requirements":{"get":{"tags":["Dinari"],"operationId":"get_dinari_us_requirements","summary":"Get outstanding US brokerage requirements","description":"Returns what a US-jurisdiction user still owes before their brokerage KYC can be submitted: field names and counts only, never answer values. Non-US users get an empty, complete requirement set. Read-only: the profile answers and regulatory consents themselves can only be given by the user in the Moneda app.","security":[{"OAuth2":["read:account"]},{"ApiKey":["read:account"]}],"responses":{"200":{"description":"What a US-jurisdiction user still owes before their brokerage KYC can be submitted. Field names and counts only — never answer values. Non-US users get an empty, complete requirement set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DinariUsRequirements"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:account scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/dinari/market-hours":{"get":{"tags":["Dinari"],"operationId":"get_market_status","summary":"Get an equity market session","description":"Returns an exchange's session clock, the US by default: which session is running, the windows of the current or next trading day, and the next session change. Pass exchange (an EODHD code such as XETRA or LSE) or symbol (SAP.XETRA) for a European exchange, which has only open and closed sessions. This is public exchange calendar data, so any authenticated caller may read it and no scope is required.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":10,"description":"EODHD exchange code: XETRA, F, LSE, PA, AS, BR, LS, MI, MC, SW, CO, ST, HE, OL, VI or IR. Default US."},"required":false,"name":"exchange","in":"query"},{"schema":{"type":"string","maxLength":32,"description":"A ticker whose suffix picks the exchange (SAP.XETRA). Ignored when exchange is set."},"required":false,"name":"symbol","in":"query"}],"responses":{"200":{"description":"The session clock: which session is running, the windows of the current or next trading day, and the next session change","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketHours"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Unknown exchange code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/dinari/orders/free-trade-allowance":{"get":{"tags":["Dinari"],"operationId":"get_free_trade_allowance","summary":"Get the remaining commission-free trades","description":"Returns how many commission-free trades the user has left in the current window and when it resets. The window follows the user's own subscription anchor, not the calendar month, so quote resetsAt. When degraded is true the count could not be read and must not be presented as fact.","security":[{"OAuth2":["read:account"]},{"ApiKey":["read:account"]}],"responses":{"200":{"description":"Commission-free trades left in the current window. The window steps from the customer's own subscription anchor, not the calendar month, so quote `resetsAt` rather than a day of the month. When `degraded` is true the count could not be read and must not be presented as fact.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FreeTradeAllowance"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:account scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/dinari/upcoming-ipos":{"get":{"tags":["Dinari"],"operationId":"get_upcoming_ipos","summary":"List upcoming IPOs","description":"Returns companies that have filed with the SEC to go public in the US (S-1 or F-1) and have not listed yet. Nothing on the list can be bought until it lists. When market data is off for the user the response is available:false with a reason.","security":[{"OAuth2":["read:account"]},{"ApiKey":["read:account"]}],"responses":{"200":{"description":"Companies heading for a US listing, newest filing first, or available:false with a reason","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpcomingIpos"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:account scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/wallet":{"get":{"tags":["Wallet"],"operationId":"get_wallet","summary":"Get the user's smart account address","description":"Returns the address of the authenticated user's self-custodial smart account, the network it lives on, the tokens it supports and the user's username, along with a warning about sending unsupported assets. `?view=lite` drops network, supportedTokens, warning and tip.","security":[{"OAuth2":["read:account"]},{"ApiKey":["read:account"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Wallet information. `?view=lite` drops network, supportedTokens, warning, tip.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/WalletInfo"},{"$ref":"#/components/schemas/WalletInfoLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The user has no active smart account (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/lifetime-savings":{"get":{"tags":["Balances"],"operationId":"get_lifetime_savings","summary":"Get lifetime savings from using Moneda","description":"Returns the user's lifetime accumulated savings from using Moneda, in their display currency: the fees Moneda covered on their behalf plus their plan's discount. `components` itemises the same total into sponsored network fees, absorbed on/off-ramp fees and the plan discount; each part is rounded independently, so they can differ from `savings` by a cent.","security":[{"OAuth2":["read:balances"]},{"ApiKey":["read:balances"]}],"responses":{"200":{"description":"Lifetime accumulated savings from using Moneda (fees Moneda covered + plan discount), in the user's display currency, with the total itemised into its three components.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LifetimeSavings"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/virtual-accounts":{"get":{"tags":["Virtual Accounts"],"operationId":"get_virtual_accounts","summary":"Get virtual bank account details","description":"Returns the user's virtual bank account details for receiving bank transfers (an IBAN for EUR, account and routing numbers for USD, a CLABE for MXN) and an overall `status`. A rail can keep its number and still be unable to receive money, so check `canReceiveDeposits` and `depositRestriction` before presenting an account as usable. `?view=lite` keeps only status, each rail's numbers, `canReceiveDeposits` and `depositRestriction`.","security":[{"OAuth2":["read:virtual_accounts"]},{"ApiKey":["read:virtual_accounts"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Virtual account details. Each rail carries `canReceiveDeposits` and, when restricted, `depositRestriction` — a rail can hold a valid number and still accept no money, so check the flag before presenting an account as usable. `?view=lite` drops capabilityInfo, minimumTransfer, firstPartyPayments, thirdPartyPayments, and the per-account `type` discriminator.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/VirtualAccounts"},{"$ref":"#/components/schemas/VirtualAccountsLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/virtual-accounts/limits":{"get":{"tags":["Virtual Accounts"],"operationId":"get_virtual_account_limits","summary":"Get bank transfer limits and verification status","description":"Returns the user's deposit and withdrawal limit usage on a rolling 52-week window, plus enhanced due diligence (EDD) status, outstanding requirements and any open compliance information requests. Crossing a limit does not reject a transfer: the banking partner pauses it pending verification, so check this before proposing a large deposit or withdrawal. Thresholds are personalised per customer and a null threshold means no enforced cap.","security":[{"OAuth2":["read:virtual_accounts"]},{"ApiKey":["read:virtual_accounts"]}],"responses":{"200":{"description":"Rolling-52-week spending limit usage per direction, plus EDD status. Crossing a limit does not reject a transfer — Iron pauses it pending verification. Thresholds are personalised per customer; never hard-code one.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VirtualAccountLimits"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/virtual-accounts/information-requests":{"get":{"tags":["Virtual Accounts"],"operationId":"get_information_requests","summary":"List compliance information requests","description":"Lists the compliance information requests (RFIs) the banking partner (Iron/MoonPay) has sent this customer, including the round currently awaiting answers. Read-only: the customer must answer personally, so there is deliberately no write endpoint. Direct the user to Settings > Features and limits in the Moneda app to respond.","security":[{"OAuth2":["read:virtual_accounts"]},{"ApiKey":["read:virtual_accounts"]}],"responses":{"200":{"description":"Compliance information requests (RFIs) Iron/MoonPay has sent this customer. Read-only: MoonPay requires the customer to answer personally, so there is deliberately no write endpoint. Direct the user to Settings > Features and limits in the Moneda app.","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/IronInformationRequest"}}},"required":["items"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/virtual-accounts/settlement-estimate":{"get":{"tags":["Virtual Accounts"],"operationId":"get_settlement_estimate","summary":"Estimate when a US bank (ACH) transfer settles","description":"Returns the date a US bank (ACH) transfer settles, accounting for the 3:00 PM ET same-day cutoff, weekends and US bank holidays. Pass `submittedAt` as an ISO timestamp, or omit it to estimate for a transfer sent now; an unparseable value returns 400. USD/ACH only. This is a pure calculation and does not read any account data.","security":[{"OAuth2":["read:virtual_accounts"]},{"ApiKey":["read:virtual_accounts"]}],"parameters":[{"schema":{"type":"string","description":"ISO timestamp of submission. Omit to estimate for a transfer sent now."},"required":false,"name":"submittedAt","in":"query"}],"responses":{"200":{"description":"When a US bank (ACH) transfer settles, accounting for Iron's 3:00 PM ET cutoff, weekends and US bank holidays. USD/ACH only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettlementEstimate"}}}},"400":{"description":"Invalid submittedAt","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/contacts":{"get":{"tags":["Contacts"],"operationId":"get_contacts","summary":"List contacts","description":"Returns the user's contacts in three groups (Moneda users, external wallets and bank accounts) with a count of each. Filter with `search` and paginate with `limit` (default 20) and `offset`. `?view=lite` drops isExternal, lastInteraction, raw wallet addresses, owner metadata and the summary counts.","security":[{"OAuth2":["read:contacts"]},{"ApiKey":["read:contacts"]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"search","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Contact list. `?view=lite` drops isExternal, lastInteraction, raw wallet addresses, owner metadata, and summary counts.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ContactResponse"},{"$ref":"#/components/schemas/ContactResponseLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/crypto/sendable":{"get":{"tags":["Crypto"],"operationId":"list_sendable_crypto","summary":"List the crypto in a Safe that can be sent out","description":"List the crypto held in a Safe that can be sent out: balance, decimals, USD value where one can be found, and whether the token is allowlisted or merely discovered on LiFi's Base list. Covers the main account, vaults and joint accounts. Use the returned contract address when proposing a send — never the symbol, because two tokens can claim the same one.","security":[{"OAuth2":["read:balances"]},{"ApiKey":["read:balances"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Vault or joint account to list. Omit for the main account's Safe."},"required":false,"name":"subAccountId","in":"query"}],"responses":{"200":{"description":"The sendable holdings","content":{"application/json":{"schema":{"type":"object","properties":{"holdings":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"symbol":{"type":"string","description":"The on-chain symbol, which a scam token can set to anything. Render the address beside it."},"displaySymbol":{"type":["string","null"],"description":"What a customer should read, e.g. 'BTC' for cbBTC. Only set for allowlisted assets."},"name":{"type":"string"},"decimals":{"type":"number"},"logoUri":{"type":["string","null"]},"balance":{"type":"string","description":"Balance in the token's own smallest unit (atoms), as a string — an 18-decimal balance exceeds 2^53."},"valueUsd":{"type":["number","null"],"description":"Null when nothing could price it — a quantity with no value."},"wasDiscovered":{"type":"boolean","description":"True for a token found on LiFi's Base list rather than Moneda's allowlist. Unverified, not vetted."}},"required":["address","symbol","displaySymbol","name","decimals","logoUri","balance","valueUsd","wasDiscovered"]}},"enabled":{"type":"boolean","description":"False when crypto transfers are off for this account. An empty list and a disabled feature are different things."}},"required":["holdings","enabled"]}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:balances scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The sub-account was not found (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/payments/crypto-send":{"post":{"tags":["Payments"],"operationId":"initiate_crypto_send","summary":"Propose sending crypto out of a Safe","description":"Send crypto out of a Safe — the main account, a vault or a joint account — to a Moneda contact or a raw Base address, for any token the account holds. Creates a pending request: nothing moves until the user reviews, approves and passkey-signs it in the Moneda app. Amounts are in the token's smallest unit (atoms). Poll GET /v1/payments/{id}/status with the returned paymentRequestId for the outcome.","security":[{"OAuth2":["write:payments"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tokenAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"Base ERC-20 contract address of the token"},"amount":{"type":"string","pattern":"^\\d+$","description":"How much to send, in the token's own smallest unit (atoms). Read `decimals` from GET /v1/crypto/sendable first — a human figure here is wrong by a power of ten.","example":"500000000000000000"},"recipientAccountId":{"type":"string","format":"uuid","description":"Moneda contact / account UUID to send to"},"recipientAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"Raw Base address, for an external send"},"subAccountId":{"type":"string","format":"uuid","description":"Vault or joint account to send FROM. Omit for the main Safe."}},"required":["tokenAddress","amount"]}}}},"responses":{"200":{"description":"Crypto send proposed, awaiting the user's approval","content":{"application/json":{"schema":{"type":"object","properties":{"paymentRequestId":{"type":"string"},"status":{"type":"string"},"amount":{"type":"string","description":"Atoms, as a string."},"currency":{"type":"string","description":"cbBTC, WETH, or OTHER_TOKEN for anything else. It does NOT identify the asset — `token.address` does."},"recipient":{"type":"string"},"paymentType":{"type":"string"},"expiresAt":{"type":"string"},"notificationSent":{"type":"boolean"},"message":{"type":"string"},"nextStep":{"type":"string"},"pollAfterMs":{"type":"number"},"token":{"type":"object","properties":{"address":{"type":"string"},"symbol":{"type":"string"},"displaySymbol":{"type":["string","null"]},"name":{"type":"string"},"decimals":{"type":"number"},"logoUri":{"type":["string","null"]},"wasDiscovered":{"type":"boolean"}},"required":["address","symbol","displaySymbol","name","decimals","logoUri","wasDiscovered"]},"valueUsd":{"type":["number","null"]},"safety":{"type":"object","properties":{"hasReport":{"type":"boolean","description":"False means nothing is on record for this token — NOT that it was checked and found clean."},"impersonation":{"type":"array","items":{}},"transferability":{"type":"array","items":{}},"sellTax":{"type":["number","null"]},"shouldWarn":{"type":"boolean"}},"required":["hasReport","impersonation","transferability","sellTax","shouldWarn"],"description":"Findings worth showing before the send. No overall grade and no all-clear: Moneda does not vouch for an asset."},"expectedShortfallWarning":{"type":["string","null"],"enum":["tax","blockable"],"description":"Set when the token is known to tax or block transfers, so the recipient may receive less than `amount`."}},"required":["paymentRequestId","status","amount","currency","recipient","paymentType","expiresAt","notificationSent","message","nextStep","pollAfterMs","token","valueUsd","safety","expectedShortfallWarning"]}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:payments scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The token, sub-account or recipient named in the request was not found (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"An identical send was requested moments ago (CONFLICT)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The send was refused. `message` is a machine-readable reason: crypto_transfers_disabled, not_listed, no_balance, amount_exceeds_balance, amount_not_positive or recipient_required (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Sending crypto is temporarily unavailable (SERVICE_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/accounts":{"get":{"tags":["Accounts"],"operationId":"get_my_accounts","summary":"List saved external wallets and bank accounts","description":"Returns the external wallets and bank accounts the user has saved as payment destinations, with a count of each. `?view=lite` drops walletCount, bankCount, and each wallet's address and provider.","security":[{"OAuth2":["read:external_accounts"]},{"ApiKey":["read:external_accounts"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"User accounts. `?view=lite` drops walletCount, bankCount, wallet.address, wallet.provider.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MyAccounts"},{"$ref":"#/components/schemas/MyAccountsLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/accounts/wallets":{"post":{"tags":["Accounts"],"operationId":"add_wallet","summary":"Save an external wallet","description":"Saves an external wallet address as a payment destination for the user. Send `confirmed: false` first: nothing is saved and the response (`success: false`, `requiresConfirmation: true`) asks you to confirm with the user that the wallet supports the right tokens on the Base network, then resend with `confirmed: true` to save it. The saved wallet is returned with its address masked. Malformed addresses, the zero address, the user's own smart account address and an address that is already saved are rejected with 422.","security":[{"OAuth2":["write:external_accounts"]},{"ApiKey":["write:external_accounts"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"walletName":{"type":"string","maxLength":200},"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"confirmed":{"type":"boolean"}},"required":["walletName","walletAddress","confirmed"]}}}},"responses":{"200":{"description":"The saved wallet (address masked), or, when `confirmed` is false, a confirmation prompt with nothing saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterWalletResponse"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The user has no active smart account (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The address is invalid, is the zero address, is the user's own smart account, or is already saved (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/accounts/banks":{"post":{"tags":["Accounts"],"operationId":"add_bank","summary":"Save an external bank account","description":"Saves a bank account as a withdrawal destination for the user, then tries to register it with the banking partner for bank withdrawals. The account is saved even when that registration is skipped or fails, and `note` says so. US accounts (ACH) require `accountNumber`, `routingNumber` and `accountType`; all other countries require a valid `iban` (SEPA). `confirmed` is ignored. Account identifiers in the response are masked.","security":[{"OAuth2":["write:external_accounts"]},{"ApiKey":["write:external_accounts"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"country":{"type":"string"},"iban":{"type":"string"},"bic":{"type":"string"},"accountNumber":{"type":"string"},"routingNumber":{"type":"string"},"accountType":{"type":"string","enum":["CHECKING","SAVINGS"]},"confirmed":{"type":"boolean","description":"Accepted for backward compatibility and ignored: unlike saving a wallet, saving a bank account has no confirmation step."}},"required":["firstName","lastName","country"]}}}},"responses":{"200":{"description":"The saved bank account, with account identifiers masked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterBankResponse"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Required bank fields are missing for the country, a field is malformed, or the account is already saved (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/settings":{"get":{"tags":["Settings"],"operationId":"get_settings","summary":"Get your settings","description":"Returns the user's app settings: language, currency, country, phone, theme, notification preferences, display options and the AI training preference.","security":[{"OAuth2":["read:settings"]},{"ApiKey":["read:settings"]}],"responses":{"200":{"description":"User settings, or only a `message` when no settings have been saved yet","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/UserSettings"},{"$ref":"#/components/schemas/UserSettingsEmpty"}]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:settings`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"tags":["Settings"],"operationId":"update_settings","summary":"Update your settings","description":"Changes the decimal separator and the marketing / product-update email and push opt-ins. Only the fields sent are changed; send at least one. Security and transactional messages always send and cannot be turned off. Returns the settings after the change.","security":[{"OAuth2":["write:profile"]},{"ApiKey":["write:profile"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSettings"}}}},"responses":{"200":{"description":"Settings after the change","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/UserSettings"},{"$ref":"#/components/schemas/UserSettingsEmpty"}]}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:profile`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No settings have been saved for this account yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"No setting was passed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/settings/help-improve-moneda":{"patch":{"tags":["Settings"],"operationId":"set_help_improve_moneda","summary":"Set your AI training preference","description":"Sets whether your conversations with Moneda AI may be used to train Moneda's own models (the Settings > Privacy toggle in the app). `enabled: false` turns it off and `enabled: true` turns it back on. The choice applies to what you send from then on: messages sent while objected stay excluded even if you turn it back on. It does not change how Moneda answers you, security logging, or quality measurement. Always on for the free Standard plan: turning it off needs a paid plan and is refused otherwise.","security":[{"OAuth2":["write:profile"]},{"ApiKey":["write:profile"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"false = object to your Moneda AI conversations being used to train Moneda's models; true = allow it (the default).","example":false}},"required":["enabled"]}}}},"responses":{"200":{"description":"Preference saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HelpImproveMoneda"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:profile`), or turning it off on the free plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No settings have been saved for this account yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/passkeys":{"get":{"tags":["Passkeys"],"operationId":"get_passkeys","summary":"List your passkeys","description":"Returns the user's passkeys: smart-account signing keys (`target=WALLET`, the default) or email login credentials (`target=LOGIN`). Addresses and credential ids are masked. `view=lite` returns a trimmed projection. It does not report which key signs next, because that preference lives only on the user's device.","security":[{"OAuth2":["read:passkeys"]},{"ApiKey":["read:passkeys"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"},{"schema":{"type":"string","enum":["WALLET","LOGIN"],"description":"WALLET (default) = smart-account signing keys, LOGIN = email login credentials"},"required":false,"description":"WALLET (default) = smart-account signing keys, LOGIN = email login credentials","name":"target","in":"query"}],"responses":{"200":{"description":"Passkey list. `?view=lite` drops the count + message envelope. Does NOT report which key signs next — that preference lives only on the user's device.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PasskeyList"},{"$ref":"#/components/schemas/PasskeyListLite"}]}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:passkeys`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/passkeys/{id}/label":{"patch":{"tags":["Passkeys"],"operationId":"set_passkey_label","summary":"Rename a passkey","description":"Sets or clears the nickname of one of your passkeys. This is a cosmetic rename only: it cannot add, remove or weaken a credential. Pass the `target` returned by GET /v1/passkeys; a null or empty `label` resets it to the provider name. Returns 404 if the passkey is not found or does not belong to you.","security":[{"OAuth2":["write:profile"]},{"ApiKey":["write:profile"]}],"parameters":[{"schema":{"type":"string","example":"b1e4c2f0-1111-2222-3333-444455556666"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"target":{"type":"string","enum":["WALLET","LOGIN"],"description":"WALLET = smart-account passkey, LOGIN = email login passkey. Use the `target` field returned by GET /v1/passkeys."},"label":{"type":["string","null"],"maxLength":40,"description":"Nickname; null or empty clears it back to the provider name"}},"required":["target","label"]}}}},"responses":{"200":{"description":"Label updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPasskeyLabelResponse"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:profile`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Passkey not found or does not belong to you","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/api-keys":{"get":{"tags":["API Keys"],"operationId":"list_api_keys","summary":"List your API keys (secrets are never returned)","description":"Returns every API key on your account, newest first, including revoked and expired ones (`isActive` is false for those), with name, prefix, scopes, expiry and last-use timestamps. The secret is stored hashed and is never returned. Authenticate with a Bearer token or with an API key in the `X-API-Key` header.","security":[{"OAuth2":["read:api_keys"]}],"responses":{"200":{"description":"API keys","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}},"required":["keys"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:api_keys`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["API Keys"],"operationId":"create_api_key","summary":"Create an API key","description":"Mints a long-lived credential for scripts, the CLI (`--api-key`), and integrations. The secret is returned once and never again. Max 10 active keys. A key can never carry a scope the caller does not already hold, and payment scopes are not available on API keys at all, because payments always require in-app approval.","security":[{"OAuth2":["write:api_keys"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"responses":{"201":{"description":"Key created — the secret is in this response only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiKey"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:api_keys`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error, scope escalation, or key limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/api-keys/{keyId}":{"delete":{"tags":["API Keys"],"operationId":"revoke_api_key","summary":"Revoke an API key","description":"Revokes one of your API keys. The key is refused on its next request and revocation cannot be undone; create a new key to restore access. Returns 404 if the key is not found and 422 if it is already revoked.","security":[{"OAuth2":["write:api_keys"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"keyId","in":"path"}],"responses":{"200":{"description":"Key revoked","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]}},"required":["success"]}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:api_keys`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Key not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The key is already revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/recovery-contacts":{"get":{"tags":["Recovery"],"operationId":"get_recovery_contacts","summary":"List your recovery contacts","description":"Returns the recovery contacts you have set up (`myContacts`) and the users who have named you as their recovery contact (`recoveryContactOf`), each with a count. `view=lite` drops `imageUrl` and the count fields.","security":[{"OAuth2":["read:recovery"]},{"ApiKey":["read:recovery"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Recovery contacts. `?view=lite` drops imageUrl and count fields.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RecoveryContacts"},{"$ref":"#/components/schemas/RecoveryContactsLite"}]}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:recovery`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/points/balance":{"get":{"tags":["Points"],"operationId":"get_points_balance","summary":"Get the points balance","description":"Returns the authenticated user's current Moneda Points total. Returns 0 for a user who has not earned any points yet.","security":[{"OAuth2":["read:points"]},{"ApiKey":["read:points"]}],"responses":{"200":{"description":"Points balance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PointsBalance"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/points/activity":{"get":{"tags":["Points"],"operationId":"get_points_activity","summary":"List points activity","description":"Returns the user's points transactions, newest first, each with its action type, points and description. Paginate with `limit` (default 10) and `offset`; `count` is the number of rows in this page, not the overall total. `?view=lite` renames createdAt to date and drops the description field.","security":[{"OAuth2":["read:points"]},{"ApiKey":["read:points"]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Points activity. `?view=lite` renames createdAt → date and drops the description field.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PointsActivityResponse"},{"$ref":"#/components/schemas/PointsActivityResponseLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/points/redemption-config":{"get":{"tags":["Points"],"operationId":"get_points_redemption_config","summary":"Get the points redemption configuration","description":"Returns what the user needs to redeem points for AI credits: the points per 1 USD of credit, the minimum redemption, the monthly cap, the user's current points balance, how much they have redeemed this month, the remaining monthly allowance and when it resets.","security":[{"OAuth2":["read:points"]},{"ApiKey":["read:points"]}],"responses":{"200":{"description":"Redemption config: rate, minimum, monthly cap, current balance, and remaining monthly allowance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PointsRedemptionConfig"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/points/redeem":{"post":{"tags":["Points"],"operationId":"redeem_points","summary":"Redeem points for AI usage credits","description":"Spends the given number of the user's Moneda Points and grants the matching prepaid AI usage budget. Fails with 422 when the amount is below the minimum, exceeds the points balance or would exceed the monthly cap. Idempotent: reuse the same `requestId` on a retry and the replay returns `granted: false`; when `requestId` is omitted the server derives the key from the user, the points and a 30 second window.","security":[{"OAuth2":["write:usage_credits"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"points":{"type":"integer","exclusiveMinimum":0,"description":"How many points to redeem for AI credits.","example":500},"requestId":{"type":"string","format":"uuid","description":"Optional idempotency key (uuid). Reuse the same value on a retry; a replay returns granted:false. If omitted the server DERIVES the key from (user, points, 30s window) instead of minting a random one, so a retry still dedupes."}},"required":["points"]}}}},"responses":{"200":{"description":"Points redeemed for AI credits (or replay, idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PointsRedeemResult"}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Points redemption is not enabled for this user (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A request with this Idempotency-Key is still running (IDEMPOTENCY_KEY_IN_PROGRESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Below minimum, insufficient balance, or over the monthly cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/referrals/code":{"get":{"tags":["Referrals"],"operationId":"get_referral_code","summary":"Get the referral code and link","description":"Returns the user's referral code, the share link built from it and their referral count. The code is derived from the username, so the request fails with a validation error until the user has set one. `?view=lite` drops the link field, which is derivable from the code.","security":[{"OAuth2":["read:referrals"]},{"ApiKey":["read:referrals"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Referral code. `?view=lite` drops the link field (derivable from code).","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReferralInfo"},{"$ref":"#/components/schemas/ReferralInfoLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"User not found (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The user has not set a username yet, so no code can be derived (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/referrals/referees":{"get":{"tags":["Referrals"],"operationId":"get_referees","summary":"List referred users","description":"Returns every user the authenticated user has referred, newest first, with display name, status (signed_up or kyc_completed) and referral date, plus the total count. Referee emails are never exposed: `email` is always an empty string. `?view=lite` drops the email and status fields and returns at most the 25 most recent referees, while `count` stays the true total.","security":[{"OAuth2":["read:referrals"]},{"ApiKey":["read:referrals"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Referees list. `?view=lite` drops the email and status fields and caps the list at the 25 most recent referees.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RefereesResponse"},{"$ref":"#/components/schemas/RefereesResponseLite"}]}}}},"400":{"description":"The request failed validation of its query, path or body (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The credential does not carry the scope this operation requires (INSUFFICIENT_SCOPE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/knowledge/search":{"get":{"tags":["Knowledge"],"operationId":"search_knowledge","summary":"Search the FAQ knowledge base","description":"Public endpoint: no authentication is required. Searches the Moneda FAQ knowledge base and returns matching questions with their answers. `query` is at most 1000 characters, `category` filters by category slug, and `limit` is 1 to 50 (default 5).","parameters":[{"schema":{"type":"string","maxLength":1000,"description":"Search query (max 1000 characters).","example":"transfer fees"},"required":false,"name":"query","in":"query"},{"schema":{"type":"string","maxLength":100,"description":"Filter by category slug.","example":"fees"},"required":false,"name":"category","in":"query"},{"schema":{"type":"integer","description":"Max results (1-50). Defaults to 5 when omitted.","minimum":1,"maximum":50,"default":5,"example":5},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Knowledge search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeSearchResponse"}}}},"400":{"description":"Invalid limit (must be an integer between 1 and 50)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[]}},"/v1/knowledge/categories":{"get":{"tags":["Knowledge"],"operationId":"list_faq_categories","summary":"List FAQ categories","description":"Public endpoint: no authentication is required. Lists the published FAQ categories with each item's `id` and `question` (no answers). Pass an `id` to GET /v1/knowledge/items/{id} for the answer.","responses":{"200":{"description":"Published FAQ categories with each item's id and question","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeCategoriesResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[]}},"/v1/knowledge/categories/{slug}":{"get":{"tags":["Knowledge"],"operationId":"get_faq_category","summary":"Get an FAQ category","description":"Public endpoint: no authentication is required. Returns one FAQ category by slug with all of its items, including answers. Returns 404 if the category does not exist.","parameters":[{"schema":{"type":"string"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Knowledge category","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeCategoryResponse"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Category not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[]}},"/v1/knowledge/items/{id}":{"get":{"tags":["Knowledge"],"operationId":"get_faq_item","summary":"Get an FAQ item","description":"Public endpoint: no authentication is required. Returns a single FAQ item by id: question, answer, category name and slug, and its related items when it has any. Returns 404 if the item does not exist.","parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Knowledge item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeItem"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[]}},"/v1/users/search":{"get":{"tags":["Users"],"operationId":"search_users","summary":"Search Moneda users","description":"Searches Moneda users by username or display name and returns each match's username and display name. `query` must be at least 3 characters. The caller is excluded from the results, and searches are rate limited to 25 per hour per user.","security":[{"OAuth2":["read:users"]},{"ApiKey":["read:users"]}],"parameters":[{"schema":{"type":"string","minLength":3},"required":true,"name":"query","in":"query"}],"responses":{"200":{"description":"User search results","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSearchResponse"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:users`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"User search rate limit exceeded (25 per hour)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}}}},"/v1/profile":{"patch":{"tags":["Profile"],"operationId":"update_display_name","summary":"Update your display name","description":"Sets the authenticated user's display name. The value is trimmed and must be 1 to 100 characters.","security":[{"OAuth2":["write:profile"]},{"ApiKey":["write:profile"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"displayName":{"type":"string","minLength":1,"maxLength":100}},"required":["displayName"]}}}},"responses":{"200":{"description":"Profile updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:profile`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Display name is empty after trimming","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/payments":{"post":{"tags":["Payments"],"operationId":"initiate_payment","summary":"Initiate a payment for the user to approve","description":"Creates a pending payment request (a transfer to a Moneda user, the user's own bank account, a saved contact's bank or wallet, or a token sale via sell_token). Nothing moves until the user approves it in the Moneda app with their passkey; the request expires after 5 minutes. Poll GET /v1/payments/{id}/status with the returned paymentRequestId for the outcome. Requires write:payments, plus write:sub_accounts when source.kind is sub_account.","security":[{"OAuth2":["write:payments"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"paymentType":{"type":"string","enum":["transfer_moneda","transfer_account","transfer_contact_bank","transfer_contact_wallet","sell_token"]},"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,6})?$"},"currency":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]},"recipientUsername":{"type":"string"},"accountId":{"type":"string","format":"uuid"},"contactUsername":{"type":"string"},"contactAccountId":{"type":"string","format":"uuid"},"reference":{"type":"string","maxLength":140},"soldTokenAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"soldTokenAmount":{"type":"string","pattern":"^\\d+$"},"source":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["main"]}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","enum":["earnings"]}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","enum":["sub_account"]},"subAccountId":{"type":"string","format":"uuid"}},"required":["kind","subAccountId"]}],"description":"main (default), earnings (USD/EUR from the earnings vault), or sub_account (a single-signature sub-account; needs write:sub_accounts)"}},"required":["paymentType","amount","currency"]}}}},"responses":{"200":{"description":"Payment initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:payments scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The recipient, contact, account or token named in the request was not found (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A request with this Idempotency-Key is still running (IDEMPOTENCY_KEY_IN_PROGRESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request is well formed but cannot be carried out, for example an unsupported currency pair, an amount outside the limits or an unverified account (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many payment requests were created recently. Retry after the interval in `retryAfterMs` (RATE_LIMIT_EXCEEDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"Payments are temporarily unavailable for this account or corridor (SERVICE_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/payments/exchange":{"post":{"tags":["Payments"],"operationId":"initiate_currency_exchange","summary":"Exchange between two of the user's currencies","description":"Exchange money between two of your own currency accounts. Creates a pending request; nothing moves until the user approves it in the Moneda app with their passkey. Poll GET /v1/payments/{id}/status with the returned paymentRequestId for the outcome.","security":[{"OAuth2":["write:payments"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,6})?$"},"fromCurrency":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]},"toCurrency":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]}},"required":["amount","fromCurrency","toCurrency"]}}}},"responses":{"200":{"description":"Exchange request initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:payments scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A request with this Idempotency-Key is still running (IDEMPOTENCY_KEY_IN_PROGRESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request is well formed but cannot be carried out, for example an unsupported currency pair, an amount outside the limits or an unverified account (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many payment requests were created recently. Retry after the interval in `retryAfterMs` (RATE_LIMIT_EXCEEDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"Payments are temporarily unavailable for this account or corridor (SERVICE_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/payments/crypto-trade":{"post":{"tags":["Payments"],"operationId":"initiate_crypto_trade","summary":"Buy or sell a crypto asset","description":"Buy or sell a crypto asset with your own balance. Not for sending money (POST /v1/payments) or converting between currencies (POST /v1/payments/exchange). The server chooses the route and creates a pending request; the user approves and signs in the Moneda app. Poll GET /v1/payments/{id}/status with the returned paymentRequestId for the outcome.","security":[{"OAuth2":["write:trades"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"symbol":{"type":"string","minLength":1,"maxLength":20,"description":"Crypto asset symbol","example":"cbBTC"},"side":{"type":"string","enum":["BUY","SELL"]},"stableCurrency":{"type":"string","enum":["USD","EUR","CAD","MXN"],"description":"Your balance the trade pays from (BUY) or lands in (SELL)"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Amount spent: in stableCurrency on BUY (e.g. '100'), in the asset on SELL (e.g. '0.001')"}},"required":["symbol","side","stableCurrency","amount"]}}}},"responses":{"200":{"description":"Crypto trade request initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:trades scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A request with this Idempotency-Key is still running (IDEMPOTENCY_KEY_IN_PROGRESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request is well formed but cannot be carried out, for example an unsupported currency pair, an amount outside the limits or an unverified account (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many payment requests were created recently. Retry after the interval in `retryAfterMs` (RATE_LIMIT_EXCEEDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"Payments are temporarily unavailable for this account or corridor (SERVICE_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/payments/token-buy":{"post":{"tags":["Payments"],"operationId":"initiate_token_buy","summary":"Buy a Base token by contract address","description":"Buy a Base token that is not in the crypto-trade catalogue, by contract address, spending your own USD, EUR, CHF or MXN balance. Creates a pending request; the token amount is priced when the user approves and signs in the Moneda app. Poll GET /v1/payments/{id}/status with the returned paymentRequestId for the outcome.","security":[{"OAuth2":["write:trades"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tokenAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"Base ERC-20 contract address (LiFi-listed)"},"stableCurrency":{"type":"string","enum":["USD","EUR","CHF","MXN"],"description":"Your balance the buy is paid from"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,6})?$","description":"How much to spend, in stableCurrency (e.g. '25')"}},"required":["tokenAddress","stableCurrency","amount"]}}}},"responses":{"200":{"description":"Token buy request initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:trades scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The recipient, contact, account or token named in the request was not found (NOT_FOUND)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A request with this Idempotency-Key is still running (IDEMPOTENCY_KEY_IN_PROGRESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request is well formed but cannot be carried out, for example an unsupported currency pair, an amount outside the limits or an unverified account (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many payment requests were created recently. Retry after the interval in `retryAfterMs` (RATE_LIMIT_EXCEEDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"Payments are temporarily unavailable for this account or corridor (SERVICE_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/payments/earnings-move":{"post":{"tags":["Payments"],"operationId":"initiate_earnings_move","summary":"Deposit into or withdraw from an earnings vault","description":"Deposit into or withdraw from an earnings vault with your own money: Earnings (MORPHO: USD, EUR, MXN) or Smart Earnings (YO: USD, EUR). Creates a pending request; the user approves and signs in the Moneda app. Poll GET /v1/payments/{id}/status with the returned paymentRequestId for the outcome.","security":[{"OAuth2":["write:payments"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"direction":{"type":"string","enum":["DEPOSIT","WITHDRAW"]},"vaultProvider":{"type":"string","enum":["MORPHO","YO"],"description":"MORPHO = Earnings, YO = Smart Earnings"},"currency":{"type":"string","enum":["USD","EUR","MXN"]},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","example":"50.00"},"reference":{"type":"string","maxLength":140}},"required":["direction","vaultProvider","currency","amount"]}}}},"responses":{"200":{"description":"Earnings move request initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:payments scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A request with this Idempotency-Key is still running (IDEMPOTENCY_KEY_IN_PROGRESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request is well formed but cannot be carried out, for example an unsupported currency pair, an amount outside the limits or an unverified account (VALIDATION_ERROR)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many payment requests were created recently. Retry after the interval in `retryAfterMs` (RATE_LIMIT_EXCEEDED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"Payments are temporarily unavailable for this account or corridor (SERVICE_UNAVAILABLE)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/payments/{id}/status":{"get":{"tags":["Payments"],"operationId":"get_payment_status","summary":"Get the status of a payment request","description":"Returns the current status of a payment request created by any of the initiate endpoints, including the transaction id and hash once the user has approved it in the Moneda app. Poll it, honouring pollAfterMs when present, until the status is final. Requires any one of the read:payments, write:payments, write:trades or write:sub_accounts scopes.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Payment status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing all of the read:payments, write:payments, write:trades and write:sub_accounts scopes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Payment not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/x402-payments":{"post":{"tags":["Payments"],"operationId":"pay_x402","summary":"Pay an x402 (HTTP 402) invoice","description":"Pay an x402 (HTTP 402 Payment Required) invoice to a wallet address. Creates a payment request that the user approves in the Moneda app, then settles on-chain. Poll GET /v1/payments/{id}/status for the result.","security":[{"OAuth2":["write:payments"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"string","pattern":"^\\d+(\\.\\d{1,6})?$","example":"0.01"},"currency":{"type":"string","enum":["USD","EUR"],"example":"USD"},"payTo":{"type":"string","description":"Recipient wallet address from the 402 paymentRequirements.payTo","example":"0x0000000000000000000000000000000000000000"},"resource":{"type":"string","format":"uri","description":"The URL that returned HTTP 402 (audit trail + display)"},"network":{"type":"string","description":"x402 network — must match the Moneda deployment (\"base\")"},"maxAmountRequired":{"type":"string"},"reference":{"type":"string","maxLength":140}},"required":["amount","currency","payTo","resource","network"]}}}},"responses":{"200":{"description":"x402 payment initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:payments scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A request with this Idempotency-Key is still running (IDEMPOTENCY_KEY_IN_PROGRESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error (invalid payTo address, network mismatch, insufficient balance, or zero amount)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/payment-destinations":{"get":{"tags":["Payments"],"operationId":"list_supported_destinations","summary":"List supported payout destinations","description":"Lists the country, currency and rail combinations a payment can be sent to across all configured providers, with the fields required for each. Pass countrySymbol to filter to one country (use EU for SEPA).","security":[{"OAuth2":["read:external_accounts"]},{"ApiKey":["read:external_accounts"]}],"parameters":[{"schema":{"type":"string","minLength":2,"maxLength":2,"description":"Optional ISO 3166-1 alpha-2 filter (e.g. 'US', 'BR', 'CO'). Use 'EU' for SEPA destinations. Omit to list all."},"required":false,"description":"Optional ISO 3166-1 alpha-2 filter (e.g. 'US', 'BR', 'CO'). Use 'EU' for SEPA destinations. Omit to list all.","name":"countrySymbol","in":"query"}],"responses":{"200":{"description":"Supported payment destinations across all configured providers.","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number"},"destinations":{"type":"array","items":{"$ref":"#/components/schemas/PaymentDestination"}}},"required":["count","destinations"]}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:external_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/payment-quote":{"get":{"tags":["Payments"],"operationId":"get_payment_quote","summary":"Preview a quote for a currency corridor","description":"Previews the exchange rate, fee, taxes and final amounts for an onramp or offramp between USDC or EURC and a local fiat currency. Provide exactly one of amountIn or amountOut. USD and EUR corridors are not quoted here and return 422; for those, POST /v1/payments returns the quote in its approval flow. Read-only: no money moves.","security":[{"OAuth2":["read:rates"]},{"ApiKey":["read:rates"]}],"parameters":[{"schema":{"type":"string","minLength":3,"maxLength":5,"description":"Source currency. ONRAMP example: 'BRL'. OFFRAMP example: 'USDC'."},"required":true,"description":"Source currency. ONRAMP example: 'BRL'. OFFRAMP example: 'USDC'.","name":"fromCurrency","in":"query"},{"schema":{"type":"string","minLength":3,"maxLength":5,"description":"Destination currency. ONRAMP example: 'USDC'. OFFRAMP example: 'BRL'."},"required":true,"description":"Destination currency. ONRAMP example: 'USDC'. OFFRAMP example: 'BRL'.","name":"toCurrency","in":"query"},{"schema":{"type":"number","exclusiveMinimum":0,"description":"Amount to send (in fromCurrency units)."},"required":false,"description":"Amount to send (in fromCurrency units).","name":"amountIn","in":"query"},{"schema":{"type":"number","exclusiveMinimum":0,"description":"Amount to receive (in toCurrency units)."},"required":false,"description":"Amount to receive (in toCurrency units).","name":"amountOut","in":"query"}],"responses":{"200":{"description":"Quote for the requested currency corridor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentQuote"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:rates scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error: invalid amount combination, unsupported corridor, missing KYC, or USD/EUR (use POST /v1/payments instead).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sessions":{"post":{"tags":["Sessions"],"operationId":"start_session","summary":"Create a session record","description":"Records a new session for the authenticated user for the audit trail and returns its id. The session type is CLI when the User-Agent starts with `moneda-cli`, otherwise API. The request body is optional; `clientName` labels the session.","security":[{"OAuth2":["read:account"]},{"ApiKey":["read:account"]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"clientName":{"type":"string","maxLength":200}}}}}},"responses":{"201":{"description":"Session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartSessionResponse"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:account`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"description":"The session record could not be created (retryable)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/sessions/{sessionId}":{"delete":{"tags":["Sessions"],"operationId":"end_session","summary":"End a session record","description":"Marks a session created with POST /v1/sessions as ended and records its duration. Returns 404 if the session does not exist or belongs to another user.","security":[{"OAuth2":["read:account"]},{"ApiKey":["read:account"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"sessionId","in":"path"}],"responses":{"200":{"description":"Session ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndSessionResponse"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:account`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sessions/active":{"get":{"tags":["Sessions"],"operationId":"list_active_sessions","summary":"List active login sessions and connected agents","description":"Returns the user's active sign-in sessions (not revoked and still holding a refresh token; at most 50, most recently active first), plus the third-party agents and services connected to the account.","security":[{"OAuth2":["read:sessions"]}],"responses":{"200":{"description":"Active login sessions and connected agents","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListActiveSessionsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:sessions`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sessions/active/{sessionId}":{"delete":{"tags":["Sessions"],"operationId":"terminate_session","summary":"Terminate an active session","description":"Revokes one login session listed by GET /v1/sessions/active and deletes its refresh tokens, signing that device out. `success` is false (still HTTP 200) when the session was already revoked or does not belong to you.","security":[{"OAuth2":["write:sessions"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"sessionId","in":"path"}],"responses":{"200":{"description":"Session terminated (false if it was already gone)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndSessionResponse"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:sessions`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sessions/revoke-all":{"post":{"tags":["Sessions"],"operationId":"terminate_all_other_sessions","summary":"Sign out of all app and web login sessions","description":"Revokes every login session on the account (app and web sign-ins) that is not already revoked and deletes all login refresh tokens, so those devices have to sign in again. OAuth client connections and API keys are left untouched, including the credential making this call, which is not a login session. Returns the number of sessions revoked.","security":[{"OAuth2":["write:sessions"]}],"responses":{"200":{"description":"Number of other sessions terminated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeAllSessionsResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:sessions`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts":{"get":{"tags":["Sub-accounts"],"operationId":"list_sub_accounts","summary":"List sub-accounts","description":"Lists the user's sub-accounts: the ones they created plus joint accounts shared with them. CLOSED sub-accounts are omitted unless includeClosed=true. Pass view=lite for a compact response.","security":[{"OAuth2":["read:sub_accounts"]},{"ApiKey":["read:sub_accounts"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"Include CLOSED vaults in the result."},"required":false,"name":"includeClosed","in":"query"}],"responses":{"200":{"description":"The user's sub-accounts: vaults they created, plus joint accounts shared with them. `?view=lite` drops the on-chain childSafeAddress and createdAt fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSubAccountsResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts/{id}":{"get":{"tags":["Sub-accounts"],"operationId":"get_sub_account","summary":"Get a sub-account","description":"Returns one sub-account's detail, including its status, type and members. Pass view=lite for a compact response.","security":[{"OAuth2":["read:sub_accounts"]},{"ApiKey":["read:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Sub-account detail. `?view=lite` drops child/parent Safe addresses and the deployment index.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SubAccountDetail"},{"$ref":"#/components/schemas/SubAccountDetailLite"}]}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sub-account not found (or owned by another user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts/{id}/balance":{"get":{"tags":["Sub-accounts"],"operationId":"get_sub_account_balance","summary":"Get a sub-account's balances","description":"Returns the on-chain balances of one sub-account. Pass currency to filter to a single token; otherwise every enabled token is returned.","security":[{"OAuth2":["read:sub_accounts"]},{"ApiKey":["read:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"},{"schema":{"type":"string","enum":["USDC","EURC","CHFAU","CADD","MXNB"]},"required":false,"name":"currency","in":"query"}],"responses":{"200":{"description":"On-chain balances for the child Safe. Pass `currency` to filter to a single token; otherwise returns every enabled token. `?view=lite` drops the childSafeAddress.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SubAccountBalance"},{"$ref":"#/components/schemas/SubAccountBalanceLite"}]}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sub-account not found (or owned by another user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Unsupported currency.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/wealth":{"get":{"tags":["Sub-accounts"],"operationId":"get_total_wealth","summary":"Get aggregate wealth across all accounts","description":"Returns the user's aggregate wealth across the main account and every live sub-account, with per-currency totals, stock and crypto positions, and a combined USD total when everything could be priced. Requires both the read:balances and read:sub_accounts scopes.","security":[{"OAuth2":["read:balances","read:sub_accounts"]},{"ApiKey":["read:balances","read:sub_accounts"]}],"parameters":[{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Aggregate wealth across the user's main account + every live vault. USDC/EURC are cross-converted via the on-chain Aerodrome rate; CHFAU stays in its own column. `stocksUsd` is the brokerage account's stocks and ETFs at the latest delayed quote and `cryptoUsd` the crypto held in the main account and vaults at the current reference price (both 6-decimal USD, `null` when a position could not be priced). `stockPositions` and `cryptoPositions` list each held asset with its quantity, USD value and `costUsd`, what was paid for it (`valueUsd` minus `costUsd` is the unrealized gain or loss; `null` when the cost is not on record). `realizedUsd` is the profit or loss already locked in by selling part of that position (negative for a loss; `null` when it is not on record), separate from the unrealized figure. `closedStockPositions` lists stocks and ETFs the user no longer holds, each with `realizedUsd`, the profit or loss selling it locked in; `closedCryptoPositions` is the same for crypto the user bought and fully sold (listed only when the recorded swaps account for every token); neither is part of any total. `totalWealthUsd` is everything combined in 6-decimal USD (`null` when any part could not be valued). `otherCryptoPositions` lists other listed tokens found in the account at a reference price; they are not counted in any total. `?view=lite` drops the morpho/yo per-leg fields and the exchange rate.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TotalWealth"},{"$ref":"#/components/schemas/TotalWealthLite"}]}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:balances and read:sub_accounts scopes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts/{id}/proposals":{"get":{"tags":["Sub-accounts"],"operationId":"list_sub_account_proposals","summary":"List a joint account's approval requests","description":"Lists the approval requests (proposals) on a joint account. Only open requests are returned unless includeResolved=true. Read-only: approving, rejecting or executing a request happens in the Moneda app.","security":[{"OAuth2":["read:sub_accounts"]},{"ApiKey":["read:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["true","false"]},"required":false,"name":"includeResolved","in":"query"}],"responses":{"200":{"description":"Approval requests on a joint account (M-of-N). Open requests only unless `includeResolved=true`. A request whose deadline has passed but has not been swept yet reports `isExpired: true`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSubAccountProposalsResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sub-account not found (or not shared with this user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["Sub-accounts"],"operationId":"propose_sub_account_multisig_proposal","summary":"Propose a spend or withdrawal on a joint account","description":"Propose a SPEND or WITHDRAW on a joint account that needs more than one approval. The user first approves the request in the Moneda app, which creates the proposal only; co-owners then approve and execute it in the Moneda app. Creates a pending request and moves nothing by itself; poll GET /v1/payments/{id}/status with the returned paymentRequestId.","security":[{"OAuth2":["write:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","example":"25.00"},"currency":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]},"proposalType":{"type":"string","enum":["SPEND","WITHDRAW"]},"description":{"type":"string","maxLength":280},"recipientUsername":{"type":"string"},"accountId":{"type":"string","format":"uuid"},"contactUsername":{"type":"string"},"contactAccountId":{"type":"string","format":"uuid"}},"required":["amount","currency","proposalType"]}}}},"responses":{"200":{"description":"Request created; the user approves it in the Moneda app. Poll GET /v1/payments/{id}/status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sub-account not found (or not operable by this user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error (unsupported currency or asset, insufficient balance, or an operation this sub-account does not allow).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts/proposals/{proposalId}":{"get":{"tags":["Sub-accounts"],"operationId":"get_sub_account_proposal","summary":"Get a joint account approval request","description":"Returns one approval request with its per-member approval list (who approved, who rejected, and when). Read-only: approving, rejecting or executing a request happens in the Moneda app.","security":[{"OAuth2":["read:sub_accounts"]},{"ApiKey":["read:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"proposalId","in":"path"}],"responses":{"200":{"description":"One approval request with its per-member approval list (who approved, who rejected, when).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JointProposalDetail"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Request not found (or on an account not shared with this user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts/{id}/top-up":{"post":{"tags":["Sub-accounts"],"operationId":"propose_sub_account_top_up","summary":"Propose a top-up of a sub-account","description":"Propose moving money from your main account into this sub-account. The user approves and passkey-signs it in the Moneda app. Creates a pending request and moves nothing by itself; poll GET /v1/payments/{id}/status with the returned paymentRequestId.","security":[{"OAuth2":["write:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","example":"25.00"},"currency":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]}},"required":["amount","currency"]}}}},"responses":{"200":{"description":"Request created; the user approves it in the Moneda app. Poll GET /v1/payments/{id}/status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sub-account not found (or not operable by this user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error (unsupported currency or asset, insufficient balance, or an operation this sub-account does not allow).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts/{id}/withdraw":{"post":{"tags":["Sub-accounts"],"operationId":"propose_sub_account_withdraw","summary":"Propose a withdrawal from a sub-account","description":"Propose moving money from this sub-account back to your main account (single-signature sub-accounts). The user approves and passkey-signs it in the Moneda app. Creates a pending request and moves nothing by itself; poll GET /v1/payments/{id}/status with the returned paymentRequestId.","security":[{"OAuth2":["write:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","example":"25.00"},"currency":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN"]}},"required":["amount","currency"]}}}},"responses":{"200":{"description":"Request created; the user approves it in the Moneda app. Poll GET /v1/payments/{id}/status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sub-account not found (or not operable by this user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error (unsupported currency or asset, insufficient balance, or an operation this sub-account does not allow).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts/{id}/swap":{"post":{"tags":["Sub-accounts"],"operationId":"propose_sub_account_swap","summary":"Propose a swap inside a sub-account","description":"Propose a swap inside this sub-account between a currency and a crypto asset. The user approves and passkey-signs it in the Moneda app. Creates a pending request and moves nothing by itself; poll GET /v1/payments/{id}/status with the returned paymentRequestId.","security":[{"OAuth2":["write:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fromAsset":{"type":"string","minLength":1,"maxLength":20,"description":"Currency code or crypto asset symbol to sell","example":"USD"},"toAsset":{"type":"string","minLength":1,"maxLength":20,"description":"Currency code or crypto asset symbol to buy","example":"cbBTC"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Amount of fromAsset","example":"25.00"},"slippageBps":{"type":"integer","minimum":0,"maximum":200}},"required":["fromAsset","toAsset","amount"]}}}},"responses":{"200":{"description":"Request created; the user approves it in the Moneda app. Poll GET /v1/payments/{id}/status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sub-account not found (or not operable by this user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error (unsupported currency or asset, insufficient balance, or an operation this sub-account does not allow).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/sub-accounts/{id}/freeze":{"post":{"tags":["Sub-accounts"],"operationId":"propose_sub_account_freeze","summary":"Propose freezing a sub-account","description":"Propose freezing a sub-account you created. The user approves it in the Moneda app; unfreezing is app-only. Creates a pending request and moves nothing by itself; poll GET /v1/payments/{id}/status with the returned paymentRequestId.","security":[{"OAuth2":["write:sub_accounts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Request created; the user approves it in the Moneda app. Poll GET /v1/payments/{id}/status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:sub_accounts scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Sub-account not found (or not operable by this user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation error (unsupported currency or asset, insufficient balance, or an operation this sub-account does not allow).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/subscriptions/charges":{"get":{"tags":["Subscriptions"],"operationId":"list_subscription_charges","summary":"List subscription charges","description":"Returns the user's subscription charges, newest first, with cursor pagination: pass `nextCursor` as `cursor` until it is null. `limit` defaults to 25 (max 100). Returns 403 when the subscriptions feature is not enabled for the user.","security":[{"OAuth2":["read:subscriptions"]},{"ApiKey":["read:subscriptions"]}],"parameters":[{"schema":{"type":"string","description":"Page size (default 25, max 100)."},"required":false,"description":"Page size (default 25, max 100).","name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque cursor from a previous response's `nextCursor`."},"required":false,"description":"Opaque cursor from a previous response's `nextCursor`.","name":"cursor","in":"query"}],"responses":{"200":{"description":"Subscription charges owned by the authenticated user, newest first. `nextCursor` is `null` on the last page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionChargeHistoryPage"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Balance-pay subscriptions feature flag is disabled for this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/subscriptions/charges/{chargeId}/invoice":{"post":{"tags":["Subscriptions"],"operationId":"get_subscription_invoice","summary":"Generate a subscription charge invoice","description":"Generates the invoice PDF for a subscription charge, or reuses the existing one, and returns the invoice number and a presigned `downloadUrl` valid for about 1 hour. Idempotent: repeat calls return a fresh URL without re-rendering. Returns 404 if the charge does not exist or is not owned by the caller, and 403 when the subscriptions feature is not enabled for the user.","security":[{"OAuth2":["read:subscriptions"]},{"ApiKey":["read:subscriptions"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"chargeId","in":"path"}],"responses":{"200":{"description":"Invoice generated (or returned from the existing row). `downloadUrl` is a presigned S3 URL valid for ~1 hour. Idempotent — repeat calls return a fresh URL without re-rendering.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionInvoiceResult"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Balance-pay subscriptions feature flag is disabled for this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Charge not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/scheduled-transactions":{"get":{"tags":["Scheduled-transactions"],"operationId":"list_scheduled_transactions","summary":"List scheduled transactions","description":"Lists the user's scheduled (recurring) transactions. Filter with status; by default only ACTIVE, PAUSED and PENDING_ACTIVATION schedules are returned.","security":[{"OAuth2":["read:scheduled_tx"]},{"ApiKey":["read:scheduled_tx"]}],"parameters":[{"schema":{"anyOf":[{"type":"string","enum":["PENDING_ACTIVATION","ACTIVE","PAUSED","EXPIRED","CANCELLED","FAILED_ACTIVATION"]},{"type":"array","items":{"type":"string","enum":["PENDING_ACTIVATION","ACTIVE","PAUSED","EXPIRED","CANCELLED","FAILED_ACTIVATION"]}}],"description":"Filter to one or more statuses. Comma-separate or repeat the param. Default: ACTIVE + PAUSED + PENDING_ACTIVATION."},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"List of the user's scheduled transactions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListScheduledTransactionsResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:scheduled_tx scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/scheduled-transactions/upcoming":{"get":{"tags":["Scheduled-transactions"],"operationId":"list_upcoming_scheduled_executions","summary":"List upcoming scheduled executions","description":"Lists the next upcoming executions across every ACTIVE schedule, soonest first. limit is 1 to 100 and defaults to 20.","security":[{"OAuth2":["read:scheduled_tx"]},{"ApiKey":["read:scheduled_tx"]}],"parameters":[{"schema":{"type":"string","pattern":"^\\d+$","description":"Max number of executions (1–100, default 20)."},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Next upcoming executions across every ACTIVE schedule, sorted by scheduledFor ascending.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListUpcomingScheduledExecutionsResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:scheduled_tx scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/scheduled-transactions/{id}":{"get":{"tags":["Scheduled-transactions"],"operationId":"get_scheduled_transaction","summary":"Get a scheduled transaction","description":"Returns one of the user's scheduled transactions by id, including its status, cadence, next execution time and on-chain session expiry.","security":[{"OAuth2":["read:scheduled_tx"]},{"ApiKey":["read:scheduled_tx"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Scheduled transaction detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledTransactionSummary"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:scheduled_tx scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found (or owned by another user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/scheduled-transactions/{id}/history":{"get":{"tags":["Scheduled-transactions"],"operationId":"get_scheduled_transaction_history","summary":"List a schedule's past executions","description":"Returns the execution history of one schedule (completed, failed and missed executions), most recent first. Page backwards by passing the previous response's nextCursor as cursor.","security":[{"OAuth2":["read:scheduled_tx"]},{"ApiKey":["read:scheduled_tx"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","pattern":"^\\d+$"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"ISO timestamp from a previous response's `nextCursor` — returns rows strictly older than this."},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"Execution history (Transaction rows + missed events) for a single schedule. Most recent first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledTransactionHistoryPage"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:scheduled_tx scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid cursor.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/scheduled-transactions/{id}/pause":{"post":{"tags":["Scheduled-transactions"],"operationId":"pause_scheduled_transaction","summary":"Pause a scheduled transaction","description":"Pauses an ACTIVE schedule so no further executions run. This is a backend-only state change with no on-chain action, and is reversible with the resume endpoint.","security":[{"OAuth2":["write:scheduled_tx"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Schedule paused. Backend-only state flip — no on-chain action. Reversible via `/resume`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PauseScheduledTransactionResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:scheduled_tx scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found (or owned by another user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Schedule is not in ACTIVE status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Feature is in READ_ONLY mode (incident response).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/scheduled-transactions/{id}/resume":{"post":{"tags":["Scheduled-transactions"],"operationId":"resume_scheduled_transaction","summary":"Resume a paused scheduled transaction","description":"Resumes a PAUSED schedule and returns the new nextExecutionAt, which is always in the future: a window that passed while paused is skipped, never executed retroactively. Fails with 422 if the schedule is not PAUSED, if its on-chain session has expired (the user must renew it in the Moneda app first), or if its end date has already passed.","security":[{"OAuth2":["write:scheduled_tx"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Schedule resumed. `nextExecutionAt` is always in the FUTURE — a window that passed while paused is skipped, never executed retroactively.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeScheduledTransactionResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:scheduled_tx scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found (or owned by another user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Schedule is not in PAUSED status, its on-chain session has expired (use the Moneda app to renew), its end date has already passed, or its status changed during the request (retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Feature is in READ_ONLY mode (incident response).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/scheduled-transactions/propose":{"post":{"tags":["Scheduled-transactions"],"operationId":"propose_scheduled_payment","summary":"Propose a recurring payment","description":"Propose a RECURRING payment. Creates a draft the user reviews and activates by signing with their passkey in the Moneda app; nothing is sent until then. Not for one-off payments (POST /v1/payments). Track it with GET /v1/scheduled-transactions/{id}.","security":[{"OAuth2":["write:payments"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"paymentType":{"type":"string","enum":["transfer_moneda","transfer_contact_wallet"],"description":"transfer_moneda: a Moneda user (recipientUsername). transfer_contact_wallet: a saved contact's wallet on Base (contactUsername or contactAccountId)."},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Amount per payment, e.g. '50'"},"currency":{"type":"string","enum":["USD","EUR","CHF","CAD"]},"frequency":{"type":"string","enum":["DAILY","WEEKLY","BIWEEKLY","MONTHLY","QUARTERLY"]},"startDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"First payment date (YYYY-MM-DD, user's timezone)"},"endDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Last possible payment date (YYYY-MM-DD). Omit for no end."},"recipientUsername":{"type":"string","maxLength":100},"contactUsername":{"type":"string","maxLength":100},"contactAccountId":{"type":"string","maxLength":100},"reference":{"type":"string","maxLength":280}},"required":["paymentType","amount","currency","frequency","startDate"]}}}},"responses":{"200":{"description":"Draft created, waiting for the user in the app.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledPaymentProposal"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:payments scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A request with this Idempotency-Key is still running (IDEMPOTENCY_KEY_IN_PROGRESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid recipient, amount, dates or currency.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Plan limit, open-proposal limit or rate limit reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/conditional-orders":{"get":{"tags":["Conditional-orders"],"operationId":"list_conditional_orders","summary":"List crypto conditional orders","description":"Lists the user's crypto conditional orders (limit, stop-loss, take-profit and trailing-stop), newest first. Returns 403 when conditional orders are not enabled for the user. Orders cannot be created through the API: creating one needs the user's passkey in the Moneda app.","security":[{"OAuth2":["read:transactions"]},{"ApiKey":["read:transactions"]}],"responses":{"200":{"description":"The user's crypto conditional orders, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListConditionalOrdersResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing scope, or conditional orders are disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/conditional-orders/{id}":{"get":{"tags":["Conditional-orders"],"operationId":"get_conditional_order","summary":"Get a crypto conditional order","description":"Returns one of the user's crypto conditional orders by id, with its trigger, status and fill details. Returns 403 when conditional orders are not enabled for the user.","security":[{"OAuth2":["read:transactions"]},{"ApiKey":["read:transactions"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Conditional order detail.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConditionalOrder"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing scope, or conditional orders are disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found (or owned by another user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/conditional-orders/{id}/cancel":{"post":{"tags":["Conditional-orders"],"operationId":"cancel_conditional_order","summary":"Cancel a crypto conditional order","description":"Cancels an open order. A PUBLISHED order is live in the CoW order book, so it is withdrawn on-chain first and only then marked CANCELLED; if the withdrawal fails the order stays live and the call returns 500, so retry. Returns 409 for an order that is already filled, cancelled or expired. No in-app approval is needed.","security":[{"OAuth2":["write:trades"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Order cancelled. A PUBLISHED order is withdrawn from the CoW order book on-chain first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConditionalOrder"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing write:trades scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found (or owned by another user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Order is already filled, cancelled or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"description":"On-chain withdrawal failed; the order is still live. Retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/statements":{"post":{"tags":["Statements"],"operationId":"request_statement","summary":"Request a PDF balance statement","description":"Create a background job that renders a PDF balance statement for a tax year or ISO date range. Returns a `requestId` immediately; poll `GET /v1/statements/status?requestId=` until READY for the time-limited download URL. Returns 429 (RATE_LIMIT_EXCEEDED) if a same-kind statement is still in flight for that currency scope and period start.","security":[{"OAuth2":["write:tax_statements"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestStatementBody"}}}},"responses":{"200":{"description":"Statement request accepted; generation runs in the background.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestStatementResponse"}}}},"400":{"description":"Request failed schema validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid period: missing `year` or `from`; `year` mixed with `from`/`to`; `from` not before `to`; `from` before 2020-01-01; or a span over 366 days.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"A statement is already REQUESTED or PROCESSING for this (kind, currency, period start).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"Balance statements are not enabled for this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["Statements"],"operationId":"list_statements","summary":"List past statement requests","description":"Page through the user's past statement requests, newest first. Each row carries a fresh presigned `downloadUrl` while its `status` is READY or DOWNLOADED and it has not expired, or `null` otherwise.","security":[{"OAuth2":["read:tax_statements"]}],"parameters":[{"schema":{"type":"string","enum":["balance_statement"]},"required":false,"name":"kind","in":"query"},{"schema":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN","CRYPTO"],"description":"Statement filter: omit for any; otherwise USD, EUR, CHF, CAD, MXN or CRYPTO. CRYPTO lists crypto and stock trades; the currency filters leave those out, and an exchange between two currencies is on the statement of both."},"required":false,"description":"Statement filter: omit for any; otherwise USD, EUR, CHF, CAD, MXN or CRYPTO. CRYPTO lists crypto and stock trades; the currency filters leave those out, and an exchange between two currencies is on the statement of both.","name":"currency","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Opaque cursor from a previous response's `nextCursor`."},"required":false,"description":"Opaque cursor from a previous response's `nextCursor`.","name":"cursor","in":"query"}],"responses":{"200":{"description":"A page of statement requests, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListStatementsResponse"}}}},"400":{"description":"Request failed schema validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Balance statements are not enabled for this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/statements/status":{"get":{"tags":["Statements"],"operationId":"get_statement_status","summary":"Get a statement's status","description":"The statement with the given `requestId`, otherwise the user's most recent statement request (optionally scoped by kind and/or asset currency). When READY, returns a freshly presigned `downloadUrl`. Returns `null` when the user has no matching request.","security":[{"OAuth2":["read:tax_statements"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"The `requestId` returned by `POST /v1/statements`. Returns that exact statement; `kind` and `currency` are ignored."},"required":false,"description":"The `requestId` returned by `POST /v1/statements`. Returns that exact statement; `kind` and `currency` are ignored.","name":"requestId","in":"query"},{"schema":{"type":"string","enum":["balance_statement"]},"required":false,"name":"kind","in":"query"},{"schema":{"type":"string","enum":["USD","EUR","CHF","CAD","MXN","CRYPTO"],"description":"Without `requestId`: the latest statement for that filter. USD, EUR, CHF, CAD, MXN or CRYPTO. CRYPTO lists crypto and stock trades; the currency filters leave those out, and an exchange between two currencies is on the statement of both."},"required":false,"description":"Without `requestId`: the latest statement for that filter. USD, EUR, CHF, CAD, MXN or CRYPTO. CRYPTO lists crypto and stock trades; the currency filters leave those out, and an exchange between two currencies is on the statement of both.","name":"currency","in":"query"}],"responses":{"200":{"description":"The matching statement, or `null`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatementStatusResponse"}}}},"400":{"description":"Request failed schema validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Balance statements are not enabled for this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/statements/mark-downloaded":{"post":{"tags":["Statements"],"operationId":"mark_statement_downloaded","summary":"Mark a READY statement as downloaded","description":"Idempotent, status-guarded. Pass `requestId` to target one row; otherwise the most recent READY row matching the optional kind/currency filter is used. Non-READY (or already-marked) rows return `alreadyMarked: true`.","security":[{"OAuth2":["write:tax_statements"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkStatementDownloadedBody"}}}},"responses":{"200":{"description":"Downloaded status recorded (or already recorded).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkStatementDownloadedResponse"}}}},"400":{"description":"Request failed schema validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Balance statements are not enabled for this account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent-sessions":{"post":{"tags":["Agent-sessions"],"operationId":"create_agent_session","summary":"Create an agent payment session","description":"Creates a scoped autonomous payment session (one pinned recipient, a per-execution cap and a per-period cap) in PENDING_ACTIVATION. It does not activate anything: the user must open the returned activationUrl in the Moneda app and approve with their passkey to install the on-chain policy. Poll GET /v1/agent-sessions/{id} until the status is ACTIVE. The caller must authenticate as an OAuth client or with an API key, and the session is bound to that credential.","security":[{"OAuth2":["write:agent_sessions"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentSessionRequest"}}}},"responses":{"201":{"description":"Session created but NOT active. The user opens activationUrl in the Moneda app and approves with a passkey to install the on-chain policy; poll GET /v1/agent-sessions/{id} until ACTIVE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentSessionResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:agent_sessions scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid input (bad address, amount, or caps).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Agent-sessions"],"operationId":"list_agent_sessions","summary":"List agent sessions","description":"Lists the user's agent sessions, newest first, with their status, spending policy and remaining period cap. Filter to a single status with the status query parameter.","security":[{"OAuth2":["read:agent_sessions"]}],"parameters":[{"schema":{"type":"string","enum":["PENDING_ACTIVATION","ACTIVE","PAUSED","REVOKED","EXPIRED","FAILED_ACTIVATION"],"description":"Filter to a single status. Default: all of the user's."},"required":false,"name":"status","in":"query"}],"responses":{"200":{"description":"The user's agent sessions, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAgentSessionsResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:agent_sessions scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/agent-sessions/{id}/payments":{"post":{"tags":["Agent-sessions"],"operationId":"agent_execute_payment","summary":"Execute a payment within an agent session","description":"Executes a payment inside an ACTIVE session's already-installed on-chain policy, with no per-payment approval from the user. The recipient must be the session's pinned recipient and the amount must fit the per-execution and per-period caps. Only the credential that created the session can execute against it. Pass idempotencyKey to retry safely: the original transaction is returned instead of sending twice.","security":[{"OAuth2":["write:agent_sessions"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"$ref":"#/components/parameters/Idempotency-Key"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteAgentSessionRequest"}}}},"responses":{"200":{"description":"Payment submitted (or an idempotent duplicate returned). Settles on-chain shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteAgentSessionResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:agent_sessions scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Session not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Session not ACTIVE, frozen, or KYC re-verification required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid input or policy violation (out-of-allowlist, over cap).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-session execution rate cap exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"Agent sessions are disabled (kill switch — global, per-agent, or per-user). Retryable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/agent-sessions/{id}":{"get":{"tags":["Agent-sessions"],"operationId":"get_agent_session","summary":"Get an agent session","description":"Returns one agent session by id, including its 5 most recent executions. Use it to poll a new session until it becomes ACTIVE.","security":[{"OAuth2":["read:agent_sessions"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The agent session, with its 5 most recent executions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAgentSessionResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the read:agent_sessions scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found (or owned by another user).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/agent-sessions/{id}/pause":{"post":{"tags":["Agent-sessions"],"operationId":"pause_agent_session","summary":"Pause an agent session","description":"Pauses an ACTIVE session, halting execution. This is a backend-only state change, reversible with the resume endpoint.","security":[{"OAuth2":["write:agent_sessions"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Session paused. Backend-only flip — halts execution, reversible via `/resume`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PauseAgentSessionResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:agent_sessions scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found, or not ACTIVE.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/agent-sessions/{id}/resume":{"post":{"tags":["Agent-sessions"],"operationId":"resume_agent_session","summary":"Resume a paused agent session","description":"Resumes a manually paused session. Auto-paused sessions need their underlying condition cleared first, and the call returns 409 while the account is frozen or needs KYC re-verification.","security":[{"OAuth2":["write:agent_sessions"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Session resumed. Only a manual (USER) pause is resumable; auto-paused sessions need their condition cleared first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeAgentSessionResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:agent_sessions scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not resumable (not paused, or auto-paused).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Account is frozen or requires KYC re-verification.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/agent-sessions/{id}/revoke":{"post":{"tags":["Agent-sessions"],"operationId":"revoke_agent_session","summary":"Revoke an agent session","description":"Revokes a session, immediately and permanently halting all execution. The on-chain uninstall of the policy is completed separately by the user in the Moneda app.","security":[{"OAuth2":["write:agent_sessions"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Session revoked — immediately halts all execution. The on-chain uninstall is completed separately in the Moneda app.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeAgentSessionResponse"}}}},"400":{"description":"Request failed schema validation (malformed path, query or body).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing the write:agent_sessions scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found, or already revoked/expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/notifications":{"get":{"tags":["Notifications"],"operationId":"list_notifications","summary":"List in-app notifications","description":"Returns a page of the user's in-app notifications, newest first. Paginate by passing the previous page's `nextCursor` as `cursor`; `limit` is 1 to 50 (default 20). `filter=unread` returns only unread items.","security":[{"OAuth2":["read:notifications"]},{"ApiKey":["read:notifications"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","enum":["all","unread"]},"required":false,"name":"filter","in":"query"}],"responses":{"200":{"description":"Page of in-app notifications, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationsPage"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:notifications`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/notifications/unread-count":{"get":{"tags":["Notifications"],"operationId":"get_unread_notification_count","summary":"Get the unread notification count","description":"Returns the number of unread in-app notifications for the user.","security":[{"OAuth2":["read:notifications"]},{"ApiKey":["read:notifications"]}],"responses":{"200":{"description":"Number of unread notifications.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadNotificationCount"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:notifications`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/notifications/messages/{id}":{"get":{"tags":["Notifications"],"operationId":"get_moneda_message","summary":"Get a message from Moneda","description":"Returns one message from Moneda (a notification with type `MONEDA_MESSAGE`): title, body, tone, optional button, attached documents by index, and `reply` (null when no reply is asked for, else whether a document is `none`, `optional` or `required`, and an optional `prompt` saying what to write). `repliedAt` is set once the user replied.","security":[{"OAuth2":["read:notifications"]},{"ApiKey":["read:notifications"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonedaMessage"}}}},"400":{"description":"Request failed validation (path, query or body) (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:notifications`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Message not found (`NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/notifications/messages/{id}/attachments/{index}":{"get":{"tags":["Notifications"],"operationId":"get_moneda_message_attachment_url","summary":"Get a download link for a message attachment","description":"Returns a presigned download URL, valid for 10 minutes, for a document Moneda attached to a message. `index` comes from the message's `attachments` list.","security":[{"OAuth2":["read:notifications"]},{"ApiKey":["read:notifications"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":["integer","null"],"minimum":0},"required":false,"name":"index","in":"path"}],"responses":{"200":{"description":"Short-lived download link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonedaMessageAttachmentUrl"}}}},"400":{"description":"Request failed validation (path, query or body) (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:notifications`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Message or attachment not found (`NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/notifications/replies":{"get":{"tags":["Notifications"],"operationId":"list_message_replies","summary":"List replies sent to Moneda","description":"Returns the user's replies to messages from Moneda, newest first. Paginate by passing the previous page's `nextCursor` as `cursor`; `limit` is 1 to 50 (default 20).","security":[{"OAuth2":["read:notifications"]},{"ApiKey":["read:notifications"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Page of replies, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageRepliesPage"}}}},"400":{"description":"Request failed validation (path, query or body) (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:notifications`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/notifications/messages/reply-upload-url":{"post":{"tags":["Notifications"],"operationId":"request_message_reply_upload","summary":"Request a presigned upload URL for a reply document","description":"Step 1 of 2 for attaching a document to a reply. Returns a presigned S3 PUT URL, an `uploadKey` and a ready-to-run `curlCommand`, valid for 15 minutes, for a PDF, JPEG or PNG of at most 5 MB. PUT the file bytes to `uploadUrl` with the exact `Content-Type`, `Content-Length` and `x-amz-server-side-encryption: AES256` headers, then pass `uploadKey` to POST /v1/notifications/messages/{id}/reply.","security":[{"OAuth2":["write:notifications"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestMessageReplyUploadBody"}}}},"responses":{"200":{"description":"Presigned PUT URL + signed `uploadKey`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageReplyUploadSlot"}}}},"400":{"description":"Request failed validation (path, query or body) (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:notifications`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Unsupported file type or size over 5 MB (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/notifications/messages/{id}/reply":{"post":{"tags":["Notifications"],"operationId":"reply_to_moneda_message","summary":"Reply to a message from Moneda","description":"Sends the user's reply to a message that asks for one. The reply goes to the Moneda team, so send only text the user wrote or approved. One reply per message, at most 600 characters, and it cannot be edited. It can be undone for 10 seconds (`undoableUntil`) with DELETE /v1/notifications/replies/{id}. When the message takes a document, stage it first with POST /v1/notifications/messages/reply-upload-url and pass `uploadKey` and `fileName`.","security":[{"OAuth2":["write:notifications"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyToMonedaMessageBody"}}}},"responses":{"200":{"description":"The stored reply.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageReply"}}}},"400":{"description":"Request failed validation (path, query or body) (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:notifications`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Message not found (`NOT_FOUND`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The reply was refused: no reply asked for, already replied, document missing or not allowed, or the upload expired (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/notifications/replies/{id}":{"delete":{"tags":["Notifications"],"operationId":"undo_message_reply","summary":"Undo a reply to a message from Moneda","description":"Takes back a reply within 10 seconds of sending it (until the reply's `undoableUntil`). Only call this when the user asks to undo. The message goes back to needing a reply, and the reply and its document are deleted.","security":[{"OAuth2":["write:notifications"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Reply id."},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The reply was undone; the message takes a reply again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UndoMessageReplyResult"}}}},"400":{"description":"Request failed validation (path, query or body) (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:notifications`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Too late: the 10-second window has passed, or the reply doesn't exist (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/recovery-emails":{"get":{"tags":["Recovery"],"operationId":"list_recovery_emails","summary":"List recovery emails","description":"Lists the recovery emails set up for email-based account recovery. Defaults to the user's main LOCAL account when `accountId` is omitted. When none are configured the response carries a `message`. Read only: changing recovery emails requires a passkey signature in the Moneda app.","security":[{"OAuth2":["read:recovery"]},{"ApiKey":["read:recovery"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"accountId","in":"query"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"List of email-based account recovery entries. Defaults to the user's main LOCAL account when no accountId is supplied.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RecoveryEmailsList"},{"$ref":"#/components/schemas/RecoveryEmailsListLite"}]}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:recovery`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/recovery-emails/{id}":{"get":{"tags":["Recovery"],"operationId":"get_recovery_email","summary":"Get a recovery email","description":"Returns a single recovery email entry by id, including the account address it protects. Returns 404 if it does not exist or is not owned by the caller.","security":[{"OAuth2":["read:recovery"]},{"ApiKey":["read:recovery"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Detail of a single recovery email entry.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RecoveryEmailDetail"},{"$ref":"#/components/schemas/RecoveryEmailDetailLite"}]}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:recovery`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/recovery-config":{"get":{"tags":["Recovery"],"operationId":"get_recovery_config","summary":"Get the recovery configuration","description":"Returns the recovery configuration for an account: the accepted recovery emails, their total weight, and the threshold required to approve a recovery. Defaults to the user's main LOCAL account when `accountId` is omitted.","security":[{"OAuth2":["read:recovery"]},{"ApiKey":["read:recovery"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"accountId","in":"query"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Recovery configuration: accepted recovery emails, total weight, and threshold required for approval.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RecoveryConfig"},{"$ref":"#/components/schemas/RecoveryConfigLite"}]}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:recovery`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/recovery-status":{"get":{"tags":["Recovery"],"operationId":"get_recovery_status","summary":"Get the latest recovery request status","description":"Returns the most recent recovery request snapshot for the account. It is read from the database, so it can lag on-chain state slightly. `hasActiveRecovery` is false when no recovery is in flight. Defaults to the user's main LOCAL account when `accountId` is omitted.","security":[{"OAuth2":["read:recovery"]},{"ApiKey":["read:recovery"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":false,"name":"accountId","in":"query"},{"schema":{"type":"string","enum":["full","lite"],"default":"full","description":"Response shape selector. `full` (default) returns every field the service exposes. `lite` drops optional fields and derived metadata for smaller responses — useful for LLM agents and terminal rendering.","example":"lite"},"required":false,"name":"view","in":"query"}],"responses":{"200":{"description":"Most recent recovery request snapshot from the DB. The mobile app additionally polls on-chain state; agents tolerate slight lag.","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"hasActiveRecovery":{"type":"boolean","enum":[false]}},"required":["hasActiveRecovery"]},{"type":"object","properties":{"hasActiveRecovery":{"type":"boolean","enum":[true]},"recovery":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string"},"executeAfter":{"type":["string","null"]},"executeBefore":{"type":["string","null"]},"threshold":{"type":"number"},"totalRecoveryEmails":{"type":"number"},"approvedCount":{"type":"number"},"approvedWeight":{"type":"number"},"approvedBy":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"weight":{"type":"number"},"approvedAt":{"type":["string","null"]}},"required":["email","weight","approvedAt"]}},"pending":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"weight":{"type":"number"}},"required":["email","weight"]}},"isExecutable":{"type":"boolean"},"timeUntilExecutable":{"type":["number","null"]},"cancelledAt":{"type":["string","null"]},"completedAt":{"type":["string","null"]},"expiredAt":{"type":["string","null"]}},"required":["id","status","createdAt","executeAfter","executeBefore","threshold","totalRecoveryEmails","approvedCount","approvedWeight","approvedBy","pending","isExecutable","timeUntilExecutable","cancelledAt","completedAt","expiredAt"]}},"required":["hasActiveRecovery","recovery"]},{"$ref":"#/components/schemas/RecoveryStatusLite"}]}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `read:recovery`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/receipts":{"get":{"tags":["Receipts"],"operationId":"list_receipts","summary":"List receipts","description":"Lists the authenticated user's receipts, newest first. Offset-paginated: `limit` defaults to 20 (max 100) and `total` counts the whole filtered set. Deleted receipts are excluded unless `status=DELETED` is requested. Filter by `transactionHash` (on-chain hash), `status` or `needsReview`.","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"transactionHash","in":"query"},{"schema":{"type":"string","enum":["UPLOADING","PROCESSING","VALID","INVALID","DELETED"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string"},"required":false,"name":"needsReview","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Receipts owned by the authenticated user. Excludes DELETED rows by default.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListReceiptsResponse"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Receipts"],"operationId":"upload_receipt","summary":"Upload a receipt as base64","description":"Uploads a PDF, JPEG or PNG (max 5 MB after decoding) as a base64 string and runs extraction synchronously: OCR, line items and an attempt to match the receipt to a transaction. Invoice-shaped documents also create a bill (`billId`). Re-uploading the same file returns the existing receipt with `deduplicated: true`. Counts against the plan's receipt scan allowance and the upload rate limits. For large files prefer the upload-url and finalize flow.","security":[{"OAuth2":["write:receipts"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadReceiptBody"}}}},"responses":{"200":{"description":"Receipt persisted (and Bill mirror written, for invoice-shaped docs).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadReceiptResponse"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The plan's monthly receipt scan allowance is used up (`PAYMENT_REQUIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"File validation failed (unsupported MIME type, empty payload, or exceeds the size cap).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Hourly receipt upload or extraction rate limit exceeded (`RATE_LIMIT_EXCEEDED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/line-items/search":{"get":{"tags":["Receipts"],"operationId":"search_receipt_line_items","summary":"Search line items across all receipts","description":"Case-insensitive substring search over the raw and normalized descriptions of every line item on the user's receipts, newest first. `query` must be at least 2 characters; `limit` defaults to 50 (max 200). `totalCount` and `totalSpend` (summed per currency) cover the whole filtered set, not just the page. `startDate` and `endDate` filter on when the parent receipt was uploaded, not on the purchase date.","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string","minLength":2},"required":true,"name":"query","in":"query"},{"schema":{"type":"string"},"required":false,"name":"category","in":"query"},{"schema":{"type":"string"},"required":false,"name":"startDate","in":"query"},{"schema":{"type":"string"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Line items matching the query (case-insensitive substring on raw + normalized description). `totalCount` and `totalSpend` aggregate the entire filtered set, not just the page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchLineItemsResponse"}}}},"400":{"description":"Request failed schema validation (`query` shorter than 2 characters) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}":{"get":{"tags":["Receipts"],"operationId":"get_receipt","summary":"Get a receipt","description":"Returns one receipt with its extracted fields, review reasons, any suggested transaction match, and presigned URLs: `downloadUrl` for the file and `thumbnailUrl` for the rendered first page (PDFs only). The URLs expire after 1 hour, do not cache them.","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"responses":{"200":{"description":"Full receipt detail including extraction metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReceiptDetail"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Receipts"],"operationId":"delete_receipt","summary":"Delete a receipt","description":"Permanently deletes a receipt together with its line items and any Bewirtungsbeleg attached to it, and removes the stored file. This cannot be undone. Returns the `transactionId` the receipt was linked to, if any.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"responses":{"200":{"description":"Receipt hard-deleted (line items + BwB cascade)","content":{"application/json":{"schema":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"transactionId":{"type":["string","null"],"format":"uuid"}},"required":["receiptId","transactionId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}/line-items":{"get":{"tags":["Receipts"],"operationId":"get_receipt_line_items","summary":"List a receipt's line items","description":"Returns the line items parsed from one receipt, ordered by `orderIndex`. Not paginated.","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"responses":{"200":{"description":"Line items parsed from the receipt, ordered by orderIndex","content":{"application/json":{"schema":{"type":"object","properties":{"lineItems":{"type":"array","items":{"$ref":"#/components/schemas/ReceiptLineItem"}}},"required":["lineItems"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Receipts"],"operationId":"add_receipt_line_item","summary":"Add a line item to a receipt","description":"Appends a line item at the end of a receipt, typically after reviewing an extraction by hand. `currency` is a 3-letter ISO 4217 code and defaults to the receipt's extracted currency, then EUR.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","minLength":1},"quantity":{"type":["number","null"]},"unitPrice":{"type":["number","null"]},"totalPrice":{"type":"number"},"taxRate":{"type":["number","null"]},"category":{"type":["string","null"]},"currency":{"type":"string","minLength":3,"maxLength":3}},"required":["description","totalPrice"]}}}},"responses":{"200":{"description":"Line item appended at the end of the receipt","content":{"application/json":{"schema":{"type":"object","properties":{"lineItemId":{"type":"string","format":"uuid"}},"required":["lineItemId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Description is blank or `currency` is not a 3-letter ISO 4217 code (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/upload-url":{"post":{"tags":["Receipts"],"operationId":"request_receipt_upload","summary":"Request a presigned receipt upload URL","description":"Step 1 of 2 of the direct upload flow. Returns a presigned S3 PUT URL and an `uploadKey`, both valid for 10 minutes, for a PDF, JPEG or PNG of at most 5 MB. PUT the file bytes to `uploadUrl`, then call POST /v1/receipts/finalize with the `uploadKey`. Nothing is stored as a receipt until finalize runs.","security":[{"OAuth2":["write:receipts"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestReceiptUploadBody"}}}},"responses":{"200":{"description":"Presigned S3 PUT URL + signed `uploadKey` to pass back to /v1/receipts/finalize.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestReceiptUploadResponse"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation failed (unsupported MIME, size out of range, bad filename).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/receipts/finalize":{"post":{"tags":["Receipts"],"operationId":"finalize_receipt_upload","summary":"Finalize a staged receipt upload","description":"Step 2 of 2 of the direct upload flow. Reads the file staged with the `uploadKey` from POST /v1/receipts/upload-url and runs extraction synchronously: OCR, line items and an attempt to match the receipt to a transaction. Returns the same shape as POST /v1/receipts. Fails with 422 if the key expired, belongs to another user, or the file was never uploaded. Counts against the plan's receipt scan allowance and the upload rate limits.","security":[{"OAuth2":["write:receipts"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinalizeReceiptUploadBody"}}}},"responses":{"200":{"description":"Receipt persisted (and Bill mirror written, for invoice-shaped docs).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadReceiptResponse"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The plan's monthly receipt scan allowance is used up (`PAYMENT_REQUIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Upload slot expired / invalid / mismatched caller, file not present in S3, or pipeline rejected the staged file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Hourly receipt upload or extraction rate limit exceeded (`RATE_LIMIT_EXCEEDED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/import-url":{"post":{"tags":["Receipts"],"operationId":"import_receipt_from_url","summary":"Import a receipt from a URL","description":"Fetches a PDF, JPEG or PNG (max 5 MB) from an `https://` URL server-side and ingests it in a single call, with the same extraction and matching as an upload. The fetch runs behind an SSRF guard: only https is accepted, private addresses are rejected, at most 3 redirects are followed and the fetch times out after 15 seconds. Counts against the plan's receipt scan allowance and the upload rate limits.","security":[{"OAuth2":["write:receipts"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportReceiptFromUrlBody"}}}},"responses":{"200":{"description":"Receipt fetched from the URL and persisted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadReceiptResponse"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The plan's monthly receipt scan allowance is used up (`PAYMENT_REQUIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"URL rejected by the SSRF guard, fetch failed, MIME / size rejected, or pipeline validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Hourly receipt upload or extraction rate limit exceeded (`RATE_LIMIT_EXCEEDED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}/reparse":{"post":{"tags":["Receipts"],"operationId":"reparse_receipt","summary":"Re-run extraction on a receipt","description":"Re-runs OCR and extraction on the file already stored for a receipt, with no re-upload. Synchronous: the response is the snapshot after extraction (status, confidence, document type, line item count). Returns 422 for deleted receipts and receipts with no stored file. Subject to the extraction rate limit.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"responses":{"200":{"description":"Extraction re-run against the stored file; response is the post-extraction snapshot. Synchronous.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReparseReceiptResponse"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Receipt is deleted or has no stored file to re-read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Hourly receipt extraction rate limit exceeded (`RATE_LIMIT_EXCEEDED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}/attach":{"post":{"tags":["Receipts"],"operationId":"attach_receipt","summary":"Attach a receipt to a transaction","description":"Links a receipt to one of the user's transactions, identified by its transaction UUID (not the on-chain hash). Returns 409 if another receipt is already attached to that transaction: detach it first.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"transactionId":{"type":"string","format":"uuid"}},"required":["transactionId"]}}}},"responses":{"200":{"description":"Receipt linked to transaction","content":{"application/json":{"schema":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"transactionId":{"type":"string","format":"uuid"}},"required":["receiptId","transactionId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt or transaction not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Another receipt is already attached to that transaction (`CONFLICT`). Detach it first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}/detach":{"post":{"tags":["Receipts"],"operationId":"detach_receipt","summary":"Detach a receipt from its transaction","description":"Unlinks a receipt from its transaction. The receipt itself is kept.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"responses":{"200":{"description":"Receipt unlinked from its transaction","content":{"application/json":{"schema":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"}},"required":["receiptId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}/partial-payment":{"post":{"tags":["Receipts"],"operationId":"mark_receipt_partial_payment","summary":"Mark a receipt as a partial payment","description":"Marks a receipt as covering more than this one payment, such as a deposit or instalment, so a total that differs from the transaction amount stops being flagged. Replaces the `amount_mismatch` review reason with `partial_payment`. Amounts are unchanged.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"responses":{"200":{"description":"Receipt marked as a partial payment (amount_mismatch replaced by partial_payment)","content":{"application/json":{"schema":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"needsReview":{"type":"boolean"},"reviewReasons":{"type":"array","items":{"type":"string"}}},"required":["receiptId","needsReview","reviewReasons"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}/amounts":{"patch":{"tags":["Receipts"],"operationId":"update_receipt_amounts","summary":"Correct a receipt's amounts","description":"Corrects a receipt's top-line amounts: total, subtotal, tax amount, tax rate (a percentage, 19 for 19%) and tip. Omit a field to leave it unchanged, send `null` to clear it. At least one field is required and values must be non-negative. Returns the amounts after the update.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"totalAmountValue":{"type":["number","null"],"minimum":0},"subtotalValue":{"type":["number","null"],"minimum":0},"taxAmountValue":{"type":["number","null"],"minimum":0},"taxRateValue":{"type":["number","null"],"minimum":0,"maximum":100,"description":"Percentage, 0-100 (19 for 19%)"},"tipAmountValue":{"type":["number","null"],"minimum":0}}}}}},"responses":{"200":{"description":"Receipt amounts updated; returns the amounts after the change","content":{"application/json":{"schema":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"totalAmountValue":{"type":["string","null"]},"subtotalValue":{"type":["string","null"]},"taxAmountValue":{"type":["string","null"]},"taxRateValue":{"type":["string","null"]},"tipAmountValue":{"type":["string","null"]}},"required":["receiptId","totalAmountValue","subtotalValue","taxAmountValue","taxRateValue","tipAmountValue"]}}}},"400":{"description":"Request failed schema validation (malformed id, a negative amount, or a tax rate over 100) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"No amount field was given (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}/confirm-suggestion":{"post":{"tags":["Receipts"],"operationId":"confirm_receipt_suggestion","summary":"Confirm a suggested transaction match","description":"Accepts the transaction match the matcher suggested for a receipt and turns it into a link. Returns 422 if the receipt has no pending suggestion or another receipt is already attached to that transaction.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"responses":{"200":{"description":"Pending matcher-suggested link promoted to a hard link","content":{"application/json":{"schema":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"transactionId":{"type":"string","format":"uuid"}},"required":["receiptId","transactionId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"No suggestion to confirm, or another receipt already attached to that transaction","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/{receiptId}/reject-suggestion":{"post":{"tags":["Receipts"],"operationId":"reject_receipt_suggestion","summary":"Reject a suggested transaction match","description":"Clears the pending transaction match suggestion on a receipt. The receipt stays unlinked. Returns 404 if there is no suggestion to reject.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"receiptId","in":"path"}],"responses":{"200":{"description":"Pending matcher suggestion cleared","content":{"application/json":{"schema":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"}},"required":["receiptId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found, or no suggestion to reject","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/receipts/line-items/{lineItemId}":{"patch":{"tags":["Receipts"],"operationId":"update_receipt_line_item","summary":"Update a receipt line item","description":"Updates one line item: description, quantity, unit price, total price, tax rate or category. Only the fields sent are changed.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"lineItemId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","minLength":1},"quantity":{"type":["number","null"]},"unitPrice":{"type":["number","null"]},"totalPrice":{"type":"number"},"taxRate":{"type":["number","null"]},"category":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"Line item updated","content":{"application/json":{"schema":{"type":"object","properties":{"lineItemId":{"type":"string","format":"uuid"}},"required":["lineItemId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Line item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Description is blank (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Receipts"],"operationId":"delete_receipt_line_item","summary":"Delete a receipt line item","description":"Permanently removes one line item from a receipt.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"lineItemId","in":"path"}],"responses":{"200":{"description":"Line item deleted","content":{"application/json":{"schema":{"type":"object","properties":{"lineItemId":{"type":"string","format":"uuid"}},"required":["lineItemId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Line item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"The receipt API is temporarily disabled or in read-only mode (`SERVICE_UNAVAILABLE`, retryable).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/bewirtungsbelege":{"get":{"tags":["Bewirtungsbeleg"],"operationId":"list_bewirtungsbelege","summary":"List Bewirtungsbelege","description":"Lists the user's Bewirtungsbelege (German hospitality receipt overlays), newest first, with the deductible and non-deductible split and signing state. Offset-paginated: `limit` defaults to 20 (max 100) and `total` counts the whole filtered set. Filter by `transactionHash` (on-chain hash).","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"transactionHash","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Bewirtungsbeleg overlays owned by the authenticated user","content":{"application/json":{"schema":{"type":"object","properties":{"bewirtungsbelege":{"type":"array","items":{"$ref":"#/components/schemas/BewirtungsbelegSummary"}},"total":{"type":"number"}},"required":["bewirtungsbelege","total"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["Bewirtungsbeleg"],"operationId":"create_bewirtungsbeleg","summary":"Create a Bewirtungsbeleg","description":"Creates a Bewirtungsbeleg overlay on an existing receipt (one per receipt). Requires an occasion of at least 10 characters and at least 2 attendees. For totals over 250 EUR that are not internal events, the host company name and address and the restaurant tax id are also required. The deductible share is computed server-side: 70% for external events, 100% for internal ones. Passing `signatureData` marks it as signed. This stores metadata only, no PDF is generated.","security":[{"OAuth2":["write:receipts"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"receiptId":{"type":"string","format":"uuid"},"date":{"type":"string"},"location":{"type":"string","minLength":1},"occasion":{"type":"string","minLength":10},"attendees":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1},"relationship":{"type":"string","minLength":1}},"required":["name","relationship"]},"minItems":2},"subtotal":{"type":"number"},"tipAmount":{"type":"number"},"totalAmount":{"type":"number"},"hostCompanyName":{"type":"string"},"hostCompanyAddress":{"type":"string"},"restaurantTaxId":{"type":"string"},"isInternalEvent":{"type":"boolean"},"signatureData":{"type":"string"}},"required":["receiptId","date","location","occasion","attendees"]}}}},"responses":{"200":{"description":"Bewirtungsbeleg created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BewirtungsbelegDetail"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Receipt not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation failed (occasion too short, <2 attendees, missing >250 EUR fields, or BwB already exists)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/bewirtungsbelege/{bewirtungsbelegId}":{"get":{"tags":["Bewirtungsbeleg"],"operationId":"get_bewirtungsbeleg","summary":"Get a Bewirtungsbeleg","description":"Returns one Bewirtungsbeleg with full detail, including host company fields, restaurant tax id, signing time and all attendees.","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"bewirtungsbelegId","in":"path"}],"responses":{"200":{"description":"Full Bewirtungsbeleg detail with all attendees","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BewirtungsbelegDetail"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Bewirtungsbeleg not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"tags":["Bewirtungsbeleg"],"operationId":"delete_bewirtungsbeleg","summary":"Delete a Bewirtungsbeleg","description":"Permanently deletes a Bewirtungsbeleg overlay and its attendees. The underlying receipt and transaction are untouched.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"bewirtungsbelegId","in":"path"}],"responses":{"200":{"description":"Bewirtungsbeleg deleted (attendees cascade)","content":{"application/json":{"schema":{"type":"object","properties":{"bewirtungsbelegId":{"type":"string","format":"uuid"}},"required":["bewirtungsbelegId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Bewirtungsbeleg not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/eigenbelege":{"get":{"tags":["Eigenbeleg"],"operationId":"list_eigenbelege","summary":"List Eigenbelege","description":"Lists the user's Eigenbelege (German self-issued substitute receipts), newest first, with recipient, amount, reason and signing state. Offset-paginated: `limit` defaults to 20 (max 100) and `total` counts the whole filtered set. Filter by `transactionHash` (on-chain hash).","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"transactionHash","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Eigenbeleg records owned by the authenticated user","content":{"application/json":{"schema":{"type":"object","properties":{"eigenbelege":{"type":"array","items":{"$ref":"#/components/schemas/EigenbelegSummary"}},"total":{"type":"number"}},"required":["eigenbelege","total"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"tags":["Eigenbeleg"],"operationId":"create_eigenbeleg","summary":"Create an Eigenbeleg","description":"Creates an Eigenbeleg (self-issued substitute receipt) for one of the user's transactions, identified by its transaction UUID. `amount` must be positive. Returns 422 if the transaction already has an Eigenbeleg or a receipt attached. Passing `signatureData` marks it as signed. This stores metadata only, no PDF is generated.","security":[{"OAuth2":["write:receipts"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"transactionId":{"type":"string","format":"uuid"},"recipientName":{"type":"string","minLength":1},"recipientAddress":{"type":"string","minLength":1},"date":{"type":"string"},"description":{"type":"string","minLength":1},"amount":{"type":"number","exclusiveMinimum":0},"reason":{"type":"string","minLength":1},"signatureData":{"type":"string"}},"required":["transactionId","recipientName","recipientAddress","date","description","amount","reason"]}}}},"responses":{"200":{"description":"Eigenbeleg created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EigenbelegDetail"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Transaction not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Validation failed (Eigenbeleg already exists, or a receipt is already attached)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/eigenbelege/{eigenbelegId}":{"get":{"tags":["Eigenbeleg"],"operationId":"get_eigenbeleg","summary":"Get an Eigenbeleg","description":"Returns one Eigenbeleg with full detail, including the recipient address and signing time.","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"eigenbelegId","in":"path"}],"responses":{"200":{"description":"Full Eigenbeleg detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EigenbelegDetail"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Eigenbeleg not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"tags":["Eigenbeleg"],"operationId":"delete_eigenbeleg","summary":"Delete an Eigenbeleg","description":"Permanently deletes an Eigenbeleg. The underlying transaction is untouched.","security":[{"OAuth2":["write:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"eigenbelegId","in":"path"}],"responses":{"200":{"description":"Eigenbeleg deleted","content":{"application/json":{"schema":{"type":"object","properties":{"eigenbelegId":{"type":"string","format":"uuid"}},"required":["eigenbelegId"]}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `write:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Eigenbeleg not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/bills":{"get":{"tags":["Bills"],"operationId":"list_bills","summary":"List bills","description":"Lists the user's bills (the accounts payable queue), due-soonest first: `dueDate` ascending with undated bills last, then `id` descending. Cursor-paginated: `limit` defaults to 25 (max 100); pass `nextCursor` back as `cursor`, it is `null` on the last page. `status` is a comma-separated list of bill statuses and defaults to PENDING. An unknown status returns 400, a malformed cursor returns 422.","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string","description":"Comma-separated BillStatus list (e.g. PENDING,PAID). Default: PENDING."},"required":false,"description":"Comma-separated BillStatus list (e.g. PENDING,PAID). Default: PENDING.","name":"status","in":"query"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"Bills owned by the authenticated user, sorted by dueDate ASC NULLS LAST, id DESC. `nextCursor` is `null` on the last page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListBillsResponse"}}}},"400":{"description":"Unknown value in the `status` filter (`VALIDATION_ERROR`, standard error envelope)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Malformed `cursor` (`VALIDATION_ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/bills/{billId}":{"get":{"tags":["Bills"],"operationId":"get_bill","summary":"Get a bill","description":"Returns one bill with full extracted detail: the resolved merchant, amounts, due date, invoice number, vendor bank details, payment status and a presigned `downloadUrl` for the file. `downloadUrl` is `null` for bills with no stored file. The URL expires after 1 hour, do not cache it.","security":[{"OAuth2":["read:receipts"]},{"ApiKey":["read:receipts"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"billId","in":"path"}],"responses":{"200":{"description":"Full bill detail including resolved merchant + downloadUrl","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillDetail"}}}},"400":{"description":"Request failed schema validation (malformed id, missing or invalid field) (`VALIDATION_ERROR`, issues in `details.issues`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The token or API key lacks the `read:receipts` scope (`INSUFFICIENT_SCOPE`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Bill not found or not owned by caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/usage-credits/purchase":{"post":{"tags":["Usage Credits"],"operationId":"purchase_usage_credits","summary":"Redeem a payment for AI usage credits","description":"Redeems a confirmed on-chain payment to Moneda for prepaid AI usage credits. The amount is read from the payment identified by `transactionHash`, never from input, converted to a USD budget and granted exactly once. Re-calling with the same hash is safe and returns `granted: false`. This endpoint moves no money: the payment must already be completed.","security":[{"OAuth2":["write:usage_credits"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"transactionHash":{"type":"string","minLength":1,"description":"Hash of the confirmed on-chain charge to the Moneda treasury, tagged with the usage-credit reference marker.","example":"0x4a3f…"}},"required":["transactionHash"]}}}},"responses":{"200":{"description":"Credits granted (or already granted, idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageCreditPurchaseResult"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:usage_credits`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No matching credit-purchase charge found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Charge not confirmed yet, or exchange rate unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Mixed-currency, zero-value, or over-the-maximum charge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"parameters":[{"$ref":"#/components/parameters/Idempotency-Key"}]}},"/v1/usage-credits/monthly-limit":{"patch":{"tags":["Usage Credits"],"operationId":"set_ai_usage_limit","summary":"Set the monthly AI usage spend limit","description":"Sets or clears the cap on how much of the prepaid AI usage credit balance may be consumed per calendar month (resets on the 1st, UTC). Pass `monthlyLimitUsd` between 0 and 100000, or null for no limit.","security":[{"OAuth2":["write:usage_credits"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"monthlyLimitUsd":{"type":["number","null"],"minimum":0,"maximum":100000,"description":"Monthly USD cap on prepaid-balance draw-down, or null for no limit (unlimited). Resets the 1st, UTC.","example":50}},"required":["monthlyLimitUsd"]}}}},"responses":{"200":{"description":"Monthly spend limit updated (or cleared)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageCreditMonthlyLimitResult"}}}},"400":{"description":"Request failed validation (path, query or body)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Insufficient scope (requires `write:usage_credits`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Invalid limit (must be ≥ 0 and ≤ 100000, or null)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations":{"post":{"tags":["Platform Management"],"operationId":"create_platform_organization","summary":"Create a platform organization","description":"Creates a Moneda Platform organization with the given name and unique slug, and seats the caller as its OWNER. Needs the `write:platform` scope; no organization role is required. Returns 409 if the slug is already taken.","security":[{"OAuth2":["write:platform"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"slug":{"type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$","example":"acme"}},"required":["name","slug"]}}}},"responses":{"200":{"description":"Organization created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrganization"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Platform Management"],"operationId":"list_platform_organizations","summary":"List the caller's platform organizations","description":"Lists the organizations the caller is an active member of, with the caller's role in each. Needs the `read:platform` scope; no organization role is required.","security":[{"OAuth2":["read:platform"]}],"responses":{"200":{"description":"Organizations the caller belongs to","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformOrganizationWithRole"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/workspaces":{"post":{"tags":["Platform Management"],"operationId":"create_platform_workspace","summary":"Create a workspace in an organization","description":"Creates a workspace under the organization. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER, ADMIN or DEVELOPER. Non-members get 404.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"slug":{"type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$","example":"acme"}},"required":["name","slug"]}}}},"responses":{"200":{"description":"Workspace created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformWorkspace"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Platform Management"],"operationId":"list_platform_workspaces","summary":"List an organization's workspaces","description":"Lists the workspaces in the organization. Needs the `read:platform` scope, and the caller's organization role is checked on top: any active member may read; non-members get 404.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"responses":{"200":{"description":"Workspaces in the org","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformWorkspace"}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/members":{"post":{"tags":["Platform Management"],"operationId":"add_platform_member","summary":"Add a member to an organization","description":"Adds a user to the organization by user id with the given role, or re-activates and updates an existing membership. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN, and only an OWNER may grant the OWNER role or modify an existing owner's membership.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","format":"uuid"},"role":{"type":"string","enum":["OWNER","ADMIN","DEVELOPER","BILLING","COMPLIANCE","MEMBER"],"example":"DEVELOPER"}},"required":["userId","role"]}}}},"responses":{"200":{"description":"Member added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformMember"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Platform Management"],"operationId":"list_platform_members","summary":"List an organization's members","description":"Lists the members of the organization with their roles and status. Needs the `read:platform` scope, and the caller's organization role is checked on top: any active member may read; non-members get 404.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"responses":{"200":{"description":"Members of the org","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformMember"}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/invites":{"post":{"tags":["Platform Management"],"operationId":"create_platform_invite","summary":"Invite a member to an organization by email","description":"Creates a pending invite for an email address with the given role and returns the raw invite token exactly once. The invite expires after 21 days. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN, and only an OWNER may invite another OWNER.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["OWNER","ADMIN","DEVELOPER","BILLING","COMPLIANCE","MEMBER"],"example":"DEVELOPER"}},"required":["email","role"]}}}},"responses":{"200":{"description":"Invite created (token returned once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformCreatedInvite"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Platform Management"],"operationId":"list_platform_invites","summary":"List an organization's invites","description":"Lists the organization's invites with their status (PENDING, ACCEPTED or EXPIRED). The invite token is never returned. Needs the `read:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"responses":{"200":{"description":"Invites for the org","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformInvite"}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/invites/accept":{"post":{"tags":["Platform Management"],"operationId":"accept_platform_invite","summary":"Accept an organization invite","description":"Redeems a raw invite token and seats the caller as an active member of the inviting organization with the invited role. The token is single use; a missing, expired or already accepted token returns 404. Needs the `write:platform` scope; any caller holding the token may redeem it, with no existing organization role required.","security":[{"OAuth2":["write:platform"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1}},"required":["token"]}}}},"responses":{"200":{"description":"Invite accepted; caller seated as a member","content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string"},"role":{"type":"string","enum":["OWNER","ADMIN","DEVELOPER","BILLING","COMPLIANCE","MEMBER"],"example":"DEVELOPER"},"status":{"type":"string","enum":["ACTIVE"]}},"required":["organizationId","role","status"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/workspaces/{workspaceId}/keys":{"post":{"tags":["Platform Management"],"operationId":"create_platform_api_key","summary":"Issue a platform API key for a workspace","description":"Issues a workspace-scoped platform API key and returns the raw secret exactly once. `mode: \"test\"` mints a sandbox (mnd_test_) key; the default is a live key. Each workspace is capped at 20 active keys per mode, and neither money-movement scopes nor `write:platform` can be granted. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER, ADMIN or DEVELOPER.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"workspaceId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"scopes":{"type":"array","items":{"type":"string","enum":["read:account","read:balances","read:transactions","read:rates","read:points","read:referrals","read:virtual_accounts","read:contacts","read:settings","read:recovery","read:passkeys","read:external_accounts","read:knowledge","read:earnings","read:spending","read:users","read:payments","read:sub_accounts","read:notifications","write:notifications","read:receipts","read:subscriptions","read:agent_sessions","write:agent_sessions","read:sessions","write:sessions","read:tax_statements","write:tax_statements","write:profile","write:external_accounts","write:contacts","write:receipts","write:usage_credits","read:api_keys","write:api_keys","read:scheduled_tx","write:scheduled_tx","read:platform"]},"default":[]},"serviceAccountId":{"type":"string","format":"uuid"},"expiresInDays":{"type":"integer","exclusiveMinimum":0},"mode":{"type":"string","enum":["live","test"],"description":"'test' mints a sandbox (mnd_test_) key."}},"required":["name"]}}}},"responses":{"200":{"description":"Key issued (raw secret returned once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformIssuedKey"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request is well-formed but rejected by a business rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Platform Management"],"operationId":"list_platform_api_keys","summary":"List a workspace's platform API keys","description":"Lists the workspace's platform API keys as metadata only; the secret is never returned. Needs the `read:platform` scope, and the caller's organization role is checked on top: requires OWNER, ADMIN or DEVELOPER. Non-members get 404.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"workspaceId","in":"path"}],"responses":{"200":{"description":"Platform keys in the workspace","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformKeySummary"}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/keys/{keyId}/rotate":{"post":{"tags":["Platform Management"],"operationId":"rotate_platform_api_key","summary":"Rotate a platform API key","description":"Mints a replacement key (raw secret returned exactly once) and puts the old key on a grace window so it can be swapped with zero downtime. `graceHours` defaults to 24 and may be at most 720. A revoked key cannot be rotated (409). Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER, ADMIN or DEVELOPER.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"keyId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"graceHours":{"type":"integer","exclusiveMinimum":0,"maximum":720}}}}}},"responses":{"200":{"description":"Key rotated; replacement secret returned once","content":{"application/json":{"schema":{"type":"object","properties":{"replacement":{"$ref":"#/components/schemas/PlatformIssuedKey"},"rotatedKeyExpiresAt":{"type":"string"}},"required":["replacement","rotatedKeyExpiresAt"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/keys/{keyId}":{"delete":{"tags":["Platform Management"],"operationId":"revoke_platform_api_key","summary":"Revoke a platform API key","description":"Revokes a platform API key immediately. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER, ADMIN, DEVELOPER or COMPLIANCE.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"keyId","in":"path"}],"responses":{"200":{"description":"Key revoked","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"revokedAt":{"type":"string"}},"required":["id","revokedAt"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/limits":{"post":{"tags":["Platform Management"],"operationId":"set_platform_usage_limit","summary":"Create or reconfigure a usage limit","description":"Creates or updates a spend or rate limit for the organization, a workspace or a key. Upserts on (scope, targetId, limitType). `windowSeconds` sets a rolling window and `notifyThresholdPct` (1 to 99) sets a notification threshold. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string","enum":["ORG","WORKSPACE","KEY"],"example":"WORKSPACE"},"targetId":{"type":"string"},"limitType":{"type":"string","enum":["SPEND_HARD","SPEND_SOFT","RATE_RPM","RATE_TPM","RATE_RPD"],"example":"SPEND_HARD"},"value":{"type":"number","exclusiveMinimum":0},"windowSeconds":{"type":["integer","null"],"exclusiveMinimum":0},"notifyThresholdPct":{"type":["integer","null"],"minimum":1,"maximum":99}},"required":["scope","targetId","limitType","value"]}}}},"responses":{"200":{"description":"Limit created or reconfigured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUsageLimit"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request is well-formed but rejected by a business rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Platform Management"],"operationId":"list_platform_usage_limits","summary":"List an organization's usage limits","description":"Lists every limit configured for the organization, including workspace-scoped and key-scoped limits. Needs the `read:platform` scope, and the caller's organization role is checked on top: any active member may read; non-members get 404.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"responses":{"200":{"description":"Limits configured for the org","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformUsageLimit"}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/limits/{limitId}":{"delete":{"tags":["Platform Management"],"operationId":"delete_platform_usage_limit","summary":"Remove a usage limit","description":"Removes a usage limit from the organization. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"limitId","in":"path"}],"responses":{"200":{"description":"Limit removed","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/workspaces/{workspaceId}/webhooks":{"post":{"tags":["Platform Management"],"operationId":"create_platform_webhook","summary":"Register a webhook endpoint for a workspace","description":"Registers a webhook endpoint subscribed to one or more platform events and returns the signing secret exactly once. The URL must be https; localhost, internal hostnames and literal private IP addresses are rejected with 422 at registration, and the resolved address is re-checked at every delivery. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"workspaceId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":["string","null"],"maxLength":500},"enabledEvents":{"type":"array","items":{"type":"string","enum":["usage.threshold_reached","usage.limit_exceeded","usage.rate_limited","credit.low","credit.exhausted","key.created","key.revoked"],"example":"usage.threshold_reached"},"minItems":1}},"required":["url","enabledEvents"]}}}},"responses":{"200":{"description":"Endpoint created (signing secret returned once)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformCreatedWebhook"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request is well-formed but rejected by a business rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"tags":["Platform Management"],"operationId":"list_platform_webhooks","summary":"List a workspace's webhook endpoints","description":"Lists the workspace's webhook endpoints as metadata only; the signing secret is never returned. Needs the `read:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"workspaceId","in":"path"}],"responses":{"200":{"description":"Webhook endpoints in the workspace","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformWebhookEndpoint"}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/webhooks/{webhookId}":{"patch":{"tags":["Platform Management"],"operationId":"update_platform_webhook","summary":"Update a webhook endpoint","description":"Updates a webhook endpoint's url, description, enabledEvents or status (ENABLED or DISABLED). Only the fields sent are changed. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"webhookId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":["string","null"],"maxLength":500},"enabledEvents":{"type":"array","items":{"type":"string","enum":["usage.threshold_reached","usage.limit_exceeded","usage.rate_limited","credit.low","credit.exhausted","key.created","key.revoked"],"example":"usage.threshold_reached"},"minItems":1},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}}},"responses":{"200":{"description":"Endpoint updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformWebhookEndpoint"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request is well-formed but rejected by a business rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"tags":["Platform Management"],"operationId":"delete_platform_webhook","summary":"Delete a webhook endpoint","description":"Deletes a webhook endpoint along with its pending deliveries. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"webhookId","in":"path"}],"responses":{"200":{"description":"Endpoint removed","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/audit-log":{"get":{"tags":["Platform Management"],"operationId":"list_platform_audit_log","summary":"List an organization's audit log","description":"Returns a page of the organization's audit log (key, limit, webhook and member changes), newest first. Paginate by passing the previous response's `nextBefore` as `before`; `limit` defaults to 50 and may be at most 200. Needs the `read:platform` scope, and the caller's organization role is checked on top: requires OWNER, ADMIN or COMPLIANCE.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"},{"schema":{"type":"string","description":"Return only entries whose `action` equals this value exactly."},"required":false,"name":"action","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor: pass the previous response's `nextBefore` to get the next (older) page. An unparseable value is ignored and the first page is returned."},"required":false,"name":"before","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Audit log page (newest first)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformAuditLogPage"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/usage":{"get":{"tags":["Platform Management"],"operationId":"list_platform_usage_records","summary":"List an organization's usage records","description":"Returns a page of the organization's metered request log (one row per billed call), newest first, optionally filtered by workspaceId, apiKeyId or tool. Paginate by passing the previous response's `nextBefore` as `before`; `limit` defaults to 50 and may be at most 200. Money fields are decimal strings. Needs the `read:platform` scope, and the caller's organization role is checked on top: any active member may read.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"workspaceId","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"apiKeyId","in":"query"},{"schema":{"type":"string","description":"Return only records whose `tool` equals this value exactly."},"required":false,"name":"tool","in":"query"},{"schema":{"type":"string","description":"Opaque pagination cursor: pass the previous response's `nextBefore` to get the next (older) page. An unparseable value is ignored and the first page is returned."},"required":false,"name":"before","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Usage-record page (newest first)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformUsageRecordPage"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/pricing":{"get":{"tags":["Platform Management"],"operationId":"get_platform_pricing","summary":"Get the platform rate card","description":"Returns the global default rate card: one row per tool with its unit, unit price, markup multiplier and effective price per unit. Money fields are decimal strings. Any authenticated caller may read it; no OAuth scope or org role is checked.","security":[{"Bearer":[]}],"responses":{"200":{"description":"Global rate card","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlatformPricedTool"}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/contract":{"get":{"tags":["Platform Management"],"operationId":"get_platform_contract","summary":"Get an organization's enterprise contract","description":"Returns the organization's enterprise contract (committed spend, term, SLA tier, dedicated capacity flag, legal document references) plus its billing mode and data residency, or null if it has no contract. Needs the `read:platform` scope, and the caller's organization role is checked on top: any active member may read; non-members get 404.","security":[{"OAuth2":["read:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"responses":{"200":{"description":"The org's enterprise contract (null if none)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformOrganizationContract"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"put":{"tags":["Platform Management"],"operationId":"upsert_platform_contract","summary":"Create or update an organization's enterprise contract","description":"Creates or updates the organization's enterprise contract terms; all body fields are optional. `legalDocRefs` are pointers to documents, never the documents themselves. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["DRAFT","ACTIVE","EXPIRED","TERMINATED"]},"committedAmountUsd":{"type":["number","null"],"minimum":0},"termStart":{"type":["string","null"],"format":"date-time"},"termEnd":{"type":["string","null"],"format":"date-time"},"autoRenew":{"type":"boolean"},"slaTier":{"type":["string","null"],"minLength":1,"maxLength":100},"dedicatedUpstreamCapacity":{"type":"boolean"},"legalDocRefs":{"type":"array","items":{"$ref":"#/components/schemas/PlatformLegalDocRef"}},"dataResidency":{"type":["string","null"],"minLength":1,"maxLength":100}}}}}},"responses":{"200":{"description":"Contract created/updated","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/PlatformOrganizationContract"},{"type":"object"}]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request is well-formed but rejected by a business rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/platform/organizations/{orgId}/billing-mode":{"post":{"tags":["Platform Management"],"operationId":"set_platform_billing_mode","summary":"Set an organization's billing mode","description":"Sets the organization's billing mode to PREPAID or INVOICED. INVOICED requires an ACTIVE enterprise contract that is within its term and a captured billing profile (legal name, address and country); otherwise 422. Needs the `write:platform` scope, and the caller's organization role is checked on top: requires OWNER or ADMIN.","security":[{"OAuth2":["write:platform"]}],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"orgId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"billingMode":{"type":"string","enum":["PREPAID","INVOICED"]}},"required":["billingMode"]}}}},"responses":{"200":{"description":"Billing mode updated","content":{"application/json":{"schema":{"type":"object","properties":{"organizationId":{"type":"string"},"billingMode":{"type":"string","enum":["PREPAID","INVOICED"]}},"required":["organizationId","billingMode"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden: the token lacks the required scope (`INSUFFICIENT_SCOPE`), insufficient organization role, plan upgrade required, or the OAuth connection is disabled or disconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found. Also returned when the caller is not a member of the organization (existence-safe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Request is well-formed but rejected by a business rule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"webhooks":{}}