Two gaps found while answering "where can I change the API key for
existing users from a webhook":
1. The AI-answer API key ("let BotFights answer for me") could only
ever be set at the exact moment of bot creation on JoinBoutPage,
because that's the only place the bot's own secret is ever in the
browser's hands (Authorization: Bot <id>:<secret> was the sole auth
path for POST/GET/DELETE /api/bots/ai-config). An existing bot's
owner had no way back in to add, change, or remove their key later.
Fixed: new owner-scoped routes at /api/bots/:name/ai-config
(GET/POST/DELETE), authorized via verifyBotOwner (nostr JWT OR the
bot's own secret — see bot-auth.ts), reusing the same underlying
ai-bot-config storage. Wired into BotProfilePage's existing
owner-only settings area, mirroring the webhook-management section's
UX pattern (collapsed toggle, provider picker, masked key input,
configured/remove state).
2. Investigating the payout side of the same question ("can we confirm
the fighter wins all the cashu sats into their node wallet
automatically") surfaced that GET /winnings/:botId and POST
/claim/:paymentId existed on the backend but had NO frontend caller
anywhere — a Cashu payout (the common case: winner has no NWC/
Lightning-address wallet linked) minted a token that was completely
invisible in the UI.
Added a "claim your winnings" section to BotProfilePage, shown
proactively (not behind a toggle — it's the owner's own money):
lists unclaimed payouts with a CLAIM button, reveals the bearer
token once claimed with a copy-to-clipboard action and guidance to
paste it into any Cashu wallet (there's no "auto-deposit" for a
bearer token the way NWC allows for Lightning — no destination
address to push to).
Route-shadowing note: /:name/ai-config is a different segment count
than the existing bare /ai-config and /:name routes, so it can't
collide with either (unlike the /poll vs /:id and /ai-config vs /:name
bugs fixed earlier this session) — confirmed via the full route table.
13 new/updated tests in ai-config.test.ts (owner-JWT auth, wrong-owner
403, bot-secret still works via verifyBotOwner, no-auth 401). Full
server suite: 815/816 passing (only the same pre-existing CPU-load-
sensitive constant-time-comparison flake, confirmed unrelated and
passing in isolation). tsc --noEmit clean (server + frontend).
Co-Authored-By: Claude <noreply@anthropic.com>