API — botmason (wallet) router¶
backend/src/routers/botmason.py (107 lines).
APIRouter(tags=["botmason"]) — no prefix; paths mount at /user/*
(botmason.py:38). Every user gets BOTMASON_MONTHLY_CAP free message
credits per calendar month; once spent, requests fall through to
offering_balance (purchased/gifted credits, no expiry). "The
conversational chat endpoints were retired in favour of journal resonance
— this router now only exposes the wallet surface … that resonance and
its sibling features charge against" (botmason.py:1-8).
| Method | Path | Rate limit | Auth | Request DTO | Response | Success | Errors |
|---|---|---|---|---|---|---|---|
| GET | /user/balance |
— | JWT | — | BalanceResponse |
200 | — |
| GET | /user/usage |
— | JWT | — | UsageResponse |
200 | — |
| POST | /user/balance/add |
5/minute | admin (require_admin) |
BalanceAddRequest |
BalanceAddResponse |
200 | 401 (no/invalid JWT), 403 admin_required (non-admin), 403 user_not_found (TOCTOU: admin row deleted mid-request) |
Notes:
/user/usagecomputes the monthly rollover without committing (BUG-BM-015): "a GET must not mutate persistent state. The UPDATE runs inside the session so the response reflects the post-reset values" and the rollover UPDATE is idempotent (botmason.py:51-74). Response fields:monthly_messages_used,monthly_messages_remaining(clamped at 0),monthly_cap,monthly_reset_date,offering_balance(botmason.py:68-74)./user/balance/addcredits the calling admin's own wallet throughservices.wallet.add_balancewithactor_user_id=admin.id, then commits and logsbalance_added(botmason.py:77-107). Every wallet mutation lands aWalletAuditrow — see data-model/commerce-wallet.- DTOs in
backend/src/schemas/botmason.py; admin gating inbackend/src/dependencies/auth.py:39-51.
Grounded in adepthood@fbc529d, 2026-07-31.