{"openapi":"3.1.0","info":{"title":"Paynnt","description":"The Paynnt payment rail: wallets, sends, payment requests, spend policies and signed intents.","version":"0.2.0"},"paths":{"/api/auth/signup":{"post":{"tags":["auth"],"summary":"Signup","operationId":"signup_api_auth_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Signup Api Auth Signup Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/login":{"post":{"tags":["auth"],"summary":"Login","operationId":"login_api_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Login Api Auth Login Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","operationId":"logout_api_auth_logout_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Logout Api Auth Logout Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/me":{"get":{"tags":["auth"],"summary":"Me","operationId":"me_api_auth_me_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Me Api Auth Me Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/password-reset":{"post":{"tags":["auth"],"summary":"Start Password Reset","description":"Email the user a reset link if the address is registered. Always\nreturns 200 — the response is timing-invariant about whether the\nemail was known, so attackers can't enumerate accounts here.","operationId":"start_password_reset_api_auth_password_reset_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Start Password Reset Api Auth Password Reset Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/password-reset-confirm":{"post":{"tags":["auth"],"summary":"Confirm Password Reset","description":"Consume a reset token + set the new password. Returns ok=true on\nsuccess; 400 if the token is missing/expired/invalid.","operationId":"confirm_password_reset_api_auth_password_reset_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetConfirmRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Confirm Password Reset Api Auth Password Reset Confirm Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/verify-email":{"post":{"tags":["auth"],"summary":"Verify Email","description":"Consume an email verification token. Doesn't need auth — the\ntoken IS the auth (we don't want to force the user back into a\nlogin flow before they confirm).","operationId":"verify_email_api_auth_verify_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Verify Email Api Auth Verify Email Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/change-password":{"post":{"tags":["auth"],"summary":"Change Password Route","operationId":"change_password_route_api_auth_change_password_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Change Password Route Api Auth Change Password Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/change-email":{"post":{"tags":["auth"],"summary":"Change Email Route","description":"Change primary email + send a verification link to the new address.\n\n`email_verified_at` is reset to NULL — the user must re-verify the\nnew inbox. Outstanding verification tokens (which pointed at the\nold address) are invalidated.","operationId":"change_email_route_api_auth_change_email_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeEmailRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Change Email Route Api Auth Change Email Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/delete-account":{"post":{"tags":["auth"],"summary":"Delete Account Route","description":"Irreversibly delete the caller's workspace and all its data.\n\nOwner-only, password-confirmed, and requires the literal word DELETE\nas a typed confirmation — the same defense-in-depth posture as the\nother destructive account actions. Cascades across every\nworkspace-scoped table and erases the personal data of any member\nleft with no other workspace (see backend/data_retention.py).\n\nThis is the GDPR / CCPA \"delete my data\" path. It destroys\nvault-encrypted wallet custody keys and cannot be undone.","operationId":"delete_account_route_api_auth_delete_account_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAccountRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Account Route Api Auth Delete Account Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/resend-verification":{"post":{"tags":["auth"],"summary":"Resend Verification","description":"Send a fresh verification email. Requires an active session so\nwe know which inbox to use — prevents using this endpoint to spam\narbitrary addresses.","operationId":"resend_verification_api_auth_resend_verification_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Resend Verification Api Auth Resend Verification Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/api-keys":{"post":{"tags":["auth"],"summary":"Mint Key","description":"Mint a new API key for the caller's workspace. Returns the full\nkey ONCE — caller MUST store it. Subsequent listings only show the\nprefix.\n\nCookie auth only. A leaked API key must NOT be able to mint more\nkeys for itself — that's a privilege-escalation foothold.","operationId":"mint_key_api_auth_api_keys_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Mint Key Api Auth Api Keys Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["auth"],"summary":"List Keys","operationId":"list_keys_api_auth_api_keys_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Keys Api Auth Api Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/api-keys/{key_id}":{"delete":{"tags":["auth"],"summary":"Revoke Key","description":"Mark an API key as revoked. The row stays in the table for audit\npurposes — bcrypt-hash comparison at auth time only matches against\nrows where `revoked_at IS NULL`. Idempotent: re-revoking returns ok.","operationId":"revoke_key_api_auth_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer","title":"Key Id"}},{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Revoke Key Api Auth Api Keys  Key Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/2fa/status":{"get":{"tags":["auth"],"summary":"Twofa Status","description":"Returns whether 2FA is available + currently enrolled.","operationId":"twofa_status_api_auth_2fa_status_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Twofa Status Api Auth 2Fa Status Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/2fa/enroll":{"post":{"tags":["auth"],"summary":"Twofa Enroll","description":"Generate a new TOTP secret + 10 recovery codes. The plaintext\ncodes are returned ONCE — store them somewhere safe.\n\nRequires password re-authentication (audit): a stolen active cookie\nalone can no longer enroll an attacker's TOTP. The secret is\nwritten to the DB (Fernet-encrypted) but `totp_enrolled_at` stays\nNULL until the user completes /2fa/verify. Calling /2fa/enroll\nagain before verifying replaces the pending secret — useful if the\nuser lost their QR scan.","operationId":"twofa_enroll_api_auth_2fa_enroll_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFAEnrollRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFAEnrollResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/2fa/verify":{"post":{"tags":["auth"],"summary":"Twofa Verify","description":"Confirm the user can produce a valid TOTP code → activate 2FA.\nSubsequent logins will require a code.","operationId":"twofa_verify_api_auth_2fa_verify_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFAVerifyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Twofa Verify Api Auth 2Fa Verify Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/2fa/disable":{"post":{"tags":["auth"],"summary":"Twofa Disable","description":"Turn 2FA off. Requires password + a current valid code (we don't\nlet an attacker who stole a session disable 2FA without proving\nthey can still produce a code).","operationId":"twofa_disable_api_auth_2fa_disable_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwoFADisableRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Twofa Disable Api Auth 2Fa Disable Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/health":{"get":{"tags":["paynnt"],"summary":"Health","description":"Liveness for the Paynnt rail. The frontend uses this to decide\nwhether to show the Paynnt dashboard or a \"rail unavailable\" notice.\nAlso exposes the preflight USD threshold so the dashboard can tell\nusers when Tool #51 will run vs skip.\n\nPublic — anon-reachable. Never leaks the RPC URL's API key: the\nHelius/QuickNode credential lives in the URL query string, so we\nreturn only the scheme + host. Network + USDC mint come from\npaynnt_infra so a mainnet deploy is reported as mainnet, not\nhardcoded devnet (pre-audit bug).","operationId":"health_api_paynnt_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Api Paynnt Health Get"}}}}}}},"/api/paynnt/account":{"post":{"tags":["paynnt"],"summary":"Create Account","description":"Idempotent: if the user already has an account, return it.\nOtherwise mint a fresh devnet keypair, encrypt the secret, and\nstore. The user's existing handle is preserved; a request that\nsets a `handle` on an account that already has one is rejected\n(handles are claimed once — a future endpoint will support\nhandle rotation).","operationId":"create_account_api_paynnt_account_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccountRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Account Api Paynnt Account Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["paynnt"],"summary":"Get Account","description":"Fetch the current user's Paynnt account with live balances.\nReturns 404 if the user hasn't created an account yet — the SPA\ntreats that as the cue to show the 'Create wallet' CTA.","operationId":"get_account_api_paynnt_account_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Account Api Paynnt Account Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/resolve":{"get":{"tags":["paynnt"],"summary":"Resolve Recipient Endpoint","description":"Inline recipient resolution for the Send form preview.\n\nReturns shape:\n  {\n    \"pubkey\": str,           # always — even for raw-pubkey input\n    \"handle\": str | None,    # set if the recipient is a known Paynnt account\n    \"is_paynnt_account\": bool,\n    \"is_self\": bool,\n  }\n\nMirrors `_resolve_recipient`'s parsing but never raises 404 — instead\nit returns `is_paynnt_account=false` for unknown @handles. That lets\nthe UI distinguish 'still typing' (400 invalid) from 'resolved but\nnot a Paynnt user' (returns 200, is_paynnt_account=false).","operationId":"resolve_recipient_endpoint_api_paynnt_resolve_get","parameters":[{"name":"recipient","in":"query","required":true,"schema":{"type":"string","title":"Recipient"}},{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Resolve Recipient Endpoint Api Paynnt Resolve Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/airdrop":{"post":{"tags":["paynnt"],"summary":"Airdrop","description":"Request a devnet SOL airdrop to the caller's Paynnt account.\nReturns the transaction signature. Devnet rate-limits to ~2 SOL\nper request per hour per IP — surface the upstream error message\nso the user can react.","operationId":"airdrop_api_paynnt_airdrop_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AirdropRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Airdrop Api Paynnt Airdrop Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/policy":{"get":{"tags":["paynnt"],"summary":"Get Spend Policy","operationId":"get_spend_policy_api_paynnt_policy_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Spend Policy Api Paynnt Policy Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["paynnt"],"summary":"Put Spend Policy","description":"Upsert the workspace's spend policy. API-key actors can't write\ntheir own caps — only cookie-session users in the workspace can.","operationId":"put_spend_policy_api_paynnt_policy_put","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_SpendPolicyDTO"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Put Spend Policy Api Paynnt Policy Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/usage":{"get":{"tags":["paynnt"],"summary":"Get Usage","description":"Report the workspace's accumulated Paynnt fees.\n\nReturns:\n  - current month-to-date: count + USD cents per fee kind (send vs service)\n  - lifetime totals\n  - the 20 most-recent metered events for transparency\n\nFees rendered here come from the existing `usage_events` table where\n`tool_id=0` and `actor_kind='rail'` — the partition the\n`paynnt_billing.record_*` helpers write into.","operationId":"get_usage_api_paynnt_usage_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Usage Api Paynnt Usage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/send":{"post":{"tags":["paynnt"],"summary":"Send Payment","description":"Send SOL or USDC from the caller's Paynnt account.\n\nFlow:\n  1. Resolve the recipient (handle → pubkey, or raw pubkey).\n  2. If amount ≥ USD-equivalent threshold, run Tool #51 preflight.\n     If the disposition is `hold` / `reject` and the client hasn't\n     set `confirm_preflight: true`, return the preflight payload\n     and a 412 so the UI can show the warning. The client can\n     re-submit with `confirm_preflight: true` to override (only\n     for `hold` / `approve_with_review`; `reject` is always blocked).\n  3. Decrypt the sender's secret key, build + sign + broadcast\n     the transaction.\n  4. Record the PaynntTransaction row with the signature.","operationId":"send_payment_api_paynnt_send_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Send Payment Api Paynnt Send Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/transactions":{"get":{"tags":["paynnt"],"summary":"List Transactions","description":"Return the most recent `limit` Paynnt transactions for the\ncaller. Capped at 200 server-side to avoid runaway scrolling.","operationId":"list_transactions_api_paynnt_transactions_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Transactions Api Paynnt Transactions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/intent/sign":{"post":{"tags":["paynnt"],"summary":"Sign Payment Intent Route","description":"Merchant server calls this to mint an HMAC-signed payment intent.\n\nReturns `{token, expires_at, decoded}`. Embed `token` in the QR\ncode / URL / email button; the customer's widget forwards it to\n`/api/paynnt/send`. The token binds the recipient + asset + amount\n+ memo — a MITM'd intermediate can't rewrite any of those without\ninvalidating the signature.\n\nAuthenticated: only workspace members can mint intents on behalf\nof their workspace. `sub` on the JWT records which workspace\nminted the intent so the audit log can trace it.","operationId":"sign_payment_intent_route_api_paynnt_intent_sign_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignIntentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Sign Payment Intent Route Api Paynnt Intent Sign Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/payment-request":{"post":{"tags":["paynnt"],"summary":"Validate Payment Request","description":"Validate + enrich a merchant payment request for the embed\nwidget. Returns the resolved merchant pubkey (so the widget can\nPOST `/api/paynnt/send` with the validated recipient), the\nhandle if known, and a USD-equivalent estimate the widget can\nsurface alongside the asset amount.\n\nTwo modes — see `PaymentRequestQuery` docstring:\n  - Signed (`intent_token`): the token is the authoritative source;\n    raw `merchant`/`asset`/`amount`/`memo` are ignored.\n  - Unsigned: raw fields are used verbatim. Devnet / free-tier only.\n\nNot authenticated: anyone with the URL can call this to inspect the\npayment they're about to sign. The actual transfer at\n`/api/paynnt/send` IS authenticated + also verifies the same token.","operationId":"validate_payment_request_api_paynnt_payment_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRequestQuery"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Validate Payment Request Api Paynnt Payment Request Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/refresh-tx":{"post":{"tags":["paynnt"],"summary":"Refresh Tx","description":"Poll the Solana RPC for the current status of a pending tx and\nflip the DB row to `confirmed` / `finalized` / `failed` when the\nchain says so. The frontend calls this on a short interval after\nsubmitting a send so the UI updates without page reload.","operationId":"refresh_tx_api_paynnt_refresh_tx_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTxRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Refresh Tx Api Paynnt Refresh Tx Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/wallets/challenge":{"post":{"tags":["paynnt"],"summary":"Issue Wallet Challenge","description":"Mint a short-lived challenge JWT for the caller. The client wraps\nthis in a human-readable message and asks the wallet to sign it.","operationId":"issue_wallet_challenge_api_paynnt_wallets_challenge_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ChallengeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/wallets":{"post":{"tags":["paynnt"],"summary":"Connect Wallet","description":"Verify the signed challenge + persist the external wallet.","operationId":"connect_wallet_api_paynnt_wallets_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_WalletConnectRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Connect Wallet Api Paynnt Wallets Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["paynnt"],"summary":"List Wallets","operationId":"list_wallets_api_paynnt_wallets_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Wallets Api Paynnt Wallets Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/wallets/{wallet_id}":{"delete":{"tags":["paynnt"],"summary":"Disconnect Wallet","operationId":"disconnect_wallet_api_paynnt_wallets__wallet_id__delete","parameters":[{"name":"wallet_id","in":"path","required":true,"schema":{"type":"integer","title":"Wallet Id"}},{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Disconnect Wallet Api Paynnt Wallets  Wallet Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/gateway/status":{"get":{"tags":["paynnt"],"summary":"Gateway Status","operationId":"gateway_status_api_paynnt_gateway_status_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Gateway Status Api Paynnt Gateway Status Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/gateway/charge":{"post":{"tags":["paynnt"],"summary":"Gateway Charge","operationId":"gateway_charge_api_paynnt_gateway_charge_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ChargeBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Gateway Charge Api Paynnt Gateway Charge Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/bank/status":{"get":{"tags":["paynnt"],"summary":"Bank Status","operationId":"bank_status_api_paynnt_bank_status_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Bank Status Api Paynnt Bank Status Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/bank/accounts":{"get":{"tags":["paynnt"],"summary":"Bank Accounts","operationId":"bank_accounts_api_paynnt_bank_accounts_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Bank Accounts Api Paynnt Bank Accounts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/bank/transfer":{"post":{"tags":["paynnt"],"summary":"Bank Transfer","description":"ACH / bank-rail transfer. The live path is not wired — this\nendpoint exists so an inspector exercising the OpenAPI surface\ngets 501/503 instead of a silent 404 or a fake pending id.","operationId":"bank_transfer_api_paynnt_bank_transfer_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_TransferBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Bank Transfer Api Paynnt Bank Transfer Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/intent/execute":{"post":{"tags":["paynnt"],"summary":"Execute Payment Intent","description":"Execute a payment intent crafted by an AI agent on behalf of a user.\n\nReturns 200 with the (cached or fresh) signature on success.\nReturns 401 if not API-key authenticated, 402 on spend-policy cap,\n412 if preflight needs review, 422 if preflight rejected.","operationId":"execute_payment_intent_api_paynnt_intent_execute_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_PaymentIntentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Execute Payment Intent Api Paynnt Intent Execute Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/send/prepare":{"post":{"tags":["paynnt"],"summary":"Prepare Self Custody Send","description":"Build an unsigned Solana tx the caller's wallet can sign.\n\nRuns the same SpendPolicy + Tool #51 preflight as the custodial\n/send path. Does NOT decrypt any server-side secret — the\nsender_pubkey is provided by the caller (typically their connected\nexternal wallet).","operationId":"prepare_self_custody_send_api_paynnt_send_prepare_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_PrepareSendRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Prepare Self Custody Send Api Paynnt Send Prepare Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/paynnt/send/broadcast":{"post":{"tags":["paynnt"],"summary":"Broadcast Self Custody Send","description":"Submit a wallet-signed tx to Solana RPC and record the result.\n\nThe signed payload is trusted as-presented — the backend doesn't\nre-parse to re-validate amount/recipient. See the module-level\nself-custody note for why.","operationId":"broadcast_self_custody_send_api_paynnt_send_broadcast_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_BroadcastRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Broadcast Self Custody Send Api Paynnt Send Broadcast Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/merchants":{"post":{"tags":["merchants"],"summary":"Create","operationId":"create_api_merchants_post","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMerchantRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["merchants"],"summary":"List Merchants","operationId":"list_merchants_api_merchants_get","parameters":[{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/merchants/{merchant_id}":{"get":{"tags":["merchants"],"summary":"Get Merchant","operationId":"get_merchant_api_merchants__merchant_id__get","parameters":[{"name":"merchant_id","in":"path","required":true,"schema":{"type":"integer","title":"Merchant Id"}},{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/merchants/{merchant_id}/onboarding-link":{"post":{"tags":["merchants"],"summary":"Onboarding Link","description":"Hand back the provider's hosted onboarding URL.\n\n503 when the provider is not configured, never a placeholder URL. A\nfake link here would be worse than a failure: the merchant would\nbelieve they had onboarded.","operationId":"onboarding_link_api_merchants__merchant_id__onboarding_link_post","parameters":[{"name":"merchant_id","in":"path","required":true,"schema":{"type":"integer","title":"Merchant Id"}},{"name":"xaish_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xaish Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/merchants/webhook":{"post":{"tags":["merchants"],"summary":"Webhook","description":"The provider tells us what it decided. Signature verified first.\n\nThis is the only path that may change onboarding state, which is why\nan unverified payload is rejected outright rather than parsed\nleniently: a forged event here would mark a merchant verified.","operationId":"webhook_api_merchants_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AirdropRequest":{"properties":{"sol":{"type":"number","maximum":2.0,"minimum":0.1,"title":"Sol","default":1.0}},"type":"object","title":"AirdropRequest"},"ChangeEmailRequest":{"properties":{"current_password":{"type":"string","maxLength":255,"minLength":1,"title":"Current Password"},"new_email":{"type":"string","maxLength":255,"minLength":3,"title":"New Email"}},"type":"object","required":["current_password","new_email"],"title":"ChangeEmailRequest"},"ChangePasswordRequest":{"properties":{"current_password":{"type":"string","maxLength":255,"minLength":1,"title":"Current Password"},"new_password":{"type":"string","maxLength":255,"minLength":8,"title":"New Password"}},"type":"object","required":["current_password","new_password"],"title":"ChangePasswordRequest"},"CreateAccountRequest":{"properties":{"handle":{"anyOf":[{"type":"string","maxLength":32,"minLength":3,"pattern":"^[a-z0-9_]+$"},{"type":"null"}],"title":"Handle"}},"type":"object","title":"CreateAccountRequest"},"CreateApiKeyRequest":{"properties":{"label":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Label"}},"type":"object","title":"CreateApiKeyRequest"},"CreateMerchantRequest":{"properties":{"display_name":{"type":"string","maxLength":160,"minLength":1,"title":"Display Name"}},"type":"object","required":["display_name"],"title":"CreateMerchantRequest","description":"Deliberately minimal.\n\nA display name is all we need; everything that identifies the\nbusiness legally is the provider's to collect. Adding fields here is\nhow a platform drifts into KYC scope one convenience at a time."},"DeleteAccountRequest":{"properties":{"current_password":{"type":"string","maxLength":255,"minLength":1,"title":"Current Password"},"confirm":{"type":"string","maxLength":32,"minLength":1,"title":"Confirm"}},"type":"object","required":["current_password","confirm"],"title":"DeleteAccountRequest"},"EmailVerifyRequest":{"properties":{"token":{"type":"string","maxLength":255,"minLength":10,"title":"Token"}},"type":"object","required":["token"],"title":"EmailVerifyRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LoginRequest":{"properties":{"email":{"type":"string","maxLength":255,"minLength":3,"title":"Email"},"password":{"type":"string","maxLength":255,"minLength":1,"title":"Password"},"totp_code":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Totp Code"},"recovery_code":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Recovery Code"}},"type":"object","required":["email","password"],"title":"LoginRequest"},"PasswordResetConfirmRequest":{"properties":{"token":{"type":"string","maxLength":255,"minLength":10,"title":"Token"},"new_password":{"type":"string","maxLength":255,"minLength":8,"title":"New Password"}},"type":"object","required":["token","new_password"],"title":"PasswordResetConfirmRequest"},"PasswordResetRequest":{"properties":{"email":{"type":"string","maxLength":255,"minLength":3,"title":"Email"}},"type":"object","required":["email"],"title":"PasswordResetRequest"},"PaymentRequestQuery":{"properties":{"merchant":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Merchant"},"asset":{"type":"string","enum":["SOL","USDC"],"title":"Asset","default":"USDC"},"amount":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"null"}],"title":"Amount"},"memo":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Memo"},"return_url":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Return Url"},"intent_token":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Intent Token"}},"type":"object","title":"PaymentRequestQuery","description":"A merchant-issued Pay-with-Paynnt payment request.\n\nThe frontend widget POSTs this to validate + resolve display data\nBEFORE showing the customer a Pay button.\n\nTwo modes:\n  1. Unsigned (`intent_token` is None) — the widget passes raw\n     merchant/asset/amount/memo. Handy for devnet and free-tier\n     use. A hostile intermediate frontend CAN rewrite these\n     before the customer signs, so it's NOT safe for mainnet.\n  2. Signed (`intent_token` is set) — the merchant server minted\n     an HMAC-signed token via /api/paynnt/intent/sign. The other\n     fields are ignored; the token's fields are authoritative.\n     Tamper-proof: any rewrite fails signature verification."},"RefreshTxRequest":{"properties":{"signature":{"type":"string","maxLength":128,"minLength":64,"title":"Signature"}},"type":"object","required":["signature"],"title":"RefreshTxRequest"},"SendRequest":{"properties":{"recipient":{"type":"string","maxLength":64,"minLength":1,"title":"Recipient"},"asset":{"type":"string","enum":["SOL","USDC"],"title":"Asset","default":"USDC"},"amount":{"type":"number","exclusiveMinimum":0.0,"title":"Amount"},"memo":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Memo"},"confirm_preflight":{"type":"boolean","title":"Confirm Preflight","default":false},"intent_token":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Intent Token"}},"type":"object","required":["recipient","amount"],"title":"SendRequest","description":"Send a Paynnt payment.\n\n`recipient` accepts either a base58 Solana pubkey or a `@handle`\nthat resolves to a Paynnt account.\n`amount` is the human-readable amount (1.5 SOL, 12.34 USDC).\n`memo` is a freeform note stored alongside the tx record. It is\nNOT yet attached on-chain (Day 3 will add SPL Memo).\n`confirm_preflight` lets the client acknowledge a previous\npreflight result so we don't re-run the Claude call on submit.\nWithout it, a preflight that returned `approve_with_review` or\n`hold` causes the send to be rejected and the preflight payload\nreturned for the user to acknowledge.\n`intent_token` (OPTIONAL) — an HMAC-signed payment intent minted\nby the merchant server via /api/paynnt/intent/sign. When present,\nthe token's `recipient_pubkey` + `asset` + `amount` + `memo` are\nthe authoritative values, overriding whatever `recipient`/etc.\nthe widget sent. This is the tamper-proof path merchants use in\nproduction; without a token the endpoint still works but a hostile\nfrontend could rewrite recipient/amount before submission."},"SignIntentRequest":{"properties":{"recipient":{"type":"string","maxLength":64,"minLength":1,"title":"Recipient"},"asset":{"type":"string","enum":["SOL","USDC"],"title":"Asset","default":"USDC"},"amount":{"type":"number","exclusiveMinimum":0.0,"title":"Amount"},"memo":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Memo"},"ttl_seconds":{"type":"integer","maximum":86400.0,"minimum":60.0,"title":"Ttl Seconds","default":1800}},"type":"object","required":["recipient","amount"],"title":"SignIntentRequest","description":"Mint a signed payment intent. Called by the merchant server so\nit can embed a tamper-proof token in emails / QR codes / redirects.\n\n`recipient` accepts a handle or a raw pubkey — resolved to a pubkey\nserver-side BEFORE signing so a future handle-rename can't reroute\nan old intent.\n`ttl_seconds` — default 30 min. Clamped to [60, 86400]. Anything\noutside that range is a footgun (too short: usable customer window\ncloses mid-checkout; too long: leaked token stays drainable for\ndays)."},"SignupRequest":{"properties":{"email":{"type":"string","maxLength":255,"minLength":3,"title":"Email"},"password":{"type":"string","maxLength":255,"minLength":8,"title":"Password"},"display_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Display Name"},"workspace_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Workspace Name"}},"type":"object","required":["email","password"],"title":"SignupRequest"},"TwoFADisableRequest":{"properties":{"password":{"type":"string","maxLength":255,"minLength":1,"title":"Password"},"code":{"type":"string","maxLength":16,"minLength":4,"title":"Code"}},"type":"object","required":["password","code"],"title":"TwoFADisableRequest"},"TwoFAEnrollRequest":{"properties":{"password":{"type":"string","maxLength":255,"minLength":1,"title":"Password"}},"type":"object","required":["password"],"title":"TwoFAEnrollRequest","description":"Password re-auth on /2fa/enroll: a stolen active cookie can't\nsilently enroll an attacker's TOTP without knowing the credential.\nSame posture as /2fa/disable, which already required password."},"TwoFAEnrollResponse":{"properties":{"secret":{"type":"string","title":"Secret"},"provisioning_uri":{"type":"string","title":"Provisioning Uri"},"recovery_codes":{"items":{"type":"string"},"type":"array","title":"Recovery Codes"}},"type":"object","required":["secret","provisioning_uri","recovery_codes"],"title":"TwoFAEnrollResponse"},"TwoFAVerifyRequest":{"properties":{"code":{"type":"string","maxLength":16,"minLength":4,"title":"Code"}},"type":"object","required":["code"],"title":"TwoFAVerifyRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"_BroadcastRequest":{"properties":{"prepare_id":{"type":"string","maxLength":64,"minLength":8,"title":"Prepare Id","description":"Echo of the prepare_id from /send/prepare — for the audit trail."},"signed_tx_b64":{"type":"string","maxLength":4096,"minLength":1,"title":"Signed Tx B64","description":"Base64-encoded signed transaction bytes from the wallet adapter."}},"type":"object","required":["prepare_id","signed_tx_b64"],"title":"_BroadcastRequest"},"_ChallengeResponse":{"properties":{"challenge":{"type":"string","title":"Challenge"},"message":{"type":"string","title":"Message"},"expires_in_seconds":{"type":"integer","title":"Expires In Seconds"}},"type":"object","required":["challenge","message","expires_in_seconds"],"title":"_ChallengeResponse"},"_ChargeBody":{"properties":{"amount_minor_units":{"type":"integer","minimum":1.0,"title":"Amount Minor Units"},"currency":{"type":"string","maxLength":3,"minLength":3,"title":"Currency","default":"USD"},"preferred":{"anyOf":[{"type":"string","enum":["stripe","paypal","googlepay","applepay"]},{"type":"null"}],"title":"Preferred"},"customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Id"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key"},"memo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Memo"}},"type":"object","required":["amount_minor_units"],"title":"_ChargeBody"},"_PaymentIntentRequest":{"properties":{"intent_id":{"type":"string","maxLength":128,"minLength":8,"title":"Intent Id"},"recipient":{"type":"string","maxLength":64,"minLength":1,"title":"Recipient"},"asset":{"type":"string","enum":["SOL","USDC"],"title":"Asset","default":"USDC"},"amount":{"type":"number","exclusiveMinimum":0.0,"title":"Amount"},"memo":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Memo"},"purpose":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Purpose","description":"Short human-readable reason e.g. 'API access fee'."},"confirm_preflight":{"type":"boolean","title":"Confirm Preflight","default":false}},"type":"object","required":["intent_id","recipient","amount"],"title":"_PaymentIntentRequest","description":"An autonomous AI agent's request to pay another party.\n\n`intent_id` is a client-supplied string (UUID, ULID, whatever) that\nthe agent uses to deduplicate retries. The same (workspace, intent_id)\npair will return the original signature on retry rather than double-\nspending. Length: enough for a UUID v4 with hyphens."},"_PrepareSendRequest":{"properties":{"sender_pubkey":{"type":"string","maxLength":64,"minLength":32,"title":"Sender Pubkey","description":"The wallet pubkey that will sign the tx — usually a connected external wallet."},"recipient":{"type":"string","maxLength":64,"minLength":1,"title":"Recipient"},"asset":{"type":"string","enum":["SOL","USDC"],"title":"Asset","default":"USDC"},"amount":{"type":"number","exclusiveMinimum":0.0,"title":"Amount"},"memo":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Memo"},"confirm_preflight":{"type":"boolean","title":"Confirm Preflight","default":false}},"type":"object","required":["sender_pubkey","recipient","amount"],"title":"_PrepareSendRequest"},"_SpendPolicyDTO":{"properties":{"max_per_tx_usd_cents":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Max Per Tx Usd Cents"},"max_per_day_usd_cents":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Max Per Day Usd Cents"},"max_per_month_usd_cents":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Max Per Month Usd Cents"},"enforce_for_api_keys":{"type":"boolean","title":"Enforce For Api Keys","default":true},"enforce_for_users":{"type":"boolean","title":"Enforce For Users","default":false}},"type":"object","title":"_SpendPolicyDTO"},"_TransferBody":{"properties":{"from_external_id":{"type":"string","maxLength":128,"minLength":1,"title":"From External Id"},"to_external_id":{"type":"string","maxLength":128,"minLength":1,"title":"To External Id"},"amount_minor_units":{"type":"integer","minimum":1.0,"title":"Amount Minor Units"},"currency":{"type":"string","maxLength":3,"minLength":3,"title":"Currency","default":"USD"},"memo":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Memo"},"idempotency_key":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Idempotency Key"}},"type":"object","required":["from_external_id","to_external_id","amount_minor_units"],"title":"_TransferBody"},"_WalletConnectRequest":{"properties":{"pubkey":{"type":"string","maxLength":64,"minLength":32,"title":"Pubkey"},"message":{"type":"string","maxLength":512,"minLength":1,"title":"Message"},"signature_hex":{"type":"string","maxLength":128,"minLength":128,"title":"Signature Hex"},"provider":{"type":"string","maxLength":32,"title":"Provider","default":"phantom"},"label":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Label"}},"type":"object","required":["pubkey","message","signature_hex"],"title":"_WalletConnectRequest"}}}}