Appearance
BlazAI chat API
All under /api/v1/blaz. Route file: bz-backend/v1/routes/balz.route.js (the filename typo is in the source). Controller: v1/controllers/blaz.controller.js.
Architecture: chat request path. Product: BlazAI.
Route summary
| Method | Path | Purpose |
|---|---|---|
POST | /v1/blaz/message/stream | Send a message, receive an SSE stream |
POST | /v1/blaz/message | Same, non-streaming JSON |
GET | /v1/blaz/chat/:chatId | One conversation's messages |
GET | /v1/blaz/sessions | The wallet's chat list |
GET | /v1/blaz/suggestions | Suggestion chips |
GET | /v1/blaz/home-chips | Landing-screen chips |
There is no session cookie or bearer token
Every endpoint keys off the wallet parameter. The same wallet on a new origin sees the same history — which is exactly why the AI domain split needed almost no backend change.
It also means anyone who knows an address can read its chat list. Addresses are pseudonymous, but that is a real privacy property, not an oversight to discover later.
POST /v1/blaz/message/stream
Body
| Field | Notes |
|---|---|
message | The user's text |
wallet | Identity and history key |
chatId | Existing session; omit to start a new one |
chainId | The wallet's current chain, passed to the agent |
currency | Display currency for portfolio and price answers |
Response — SSE
Three named events, in this order:
| Event | Payload | Why |
|---|---|---|
meta | { action, data } | First, so the card renders before any prose arrives |
token | A text chunk | Repeated |
done | — | Stream complete |
Client: defi-dex/src/apis/ai.api.ts → sendAIMessageStream({ onToken, onMeta, onDone }).
STREAM_TOKEN_DELAY_MS can pace tokens artificially.
What happens server-side
- Find or create a
blazSessionfor the wallet. - Persist the user's
blazMessage. getAgentResponse(...)→POST {AGENT_URL}/chat/:sessionIdwith{ input, chain, supported_chains, activeDappProjectId? }→{ intent, data, text }.- Switch on
intent, call the handler, get{ text, data, action }. - Persist the bot's
blazMessage. - Emit SSE.
The handler re-validates everything the agent extracted
Token symbols against the Token collection, chain names against Network, then real quotes. A hallucinated token resolves to nothing. See chat request path.
POST /v1/blaz/message
Identical, returning the whole { text, data, action } at once. Useful for non-browser clients and for testing.
GET /v1/blaz/chat/:chatId
The message list for one session, in order. Each message:
js
{ session, senderType: 'user' | 'bot', text, action, data }GET /v1/blaz/sessions
Chat list for a wallet. Session shape:
js
{ wallet, name, pending, activeDappProjectId }activeDappProjectId is what makes MODIFY_DAPP, SHOW_DAPP, GO_ONCHAIN and DEPLOY_CONTRACTS selectable — it is passed to the agent with every request.
GET /v1/blaz/suggestions · GET /v1/blaz/home-chips
Prompt suggestions. On the AI site these plus the ⌘K palette are the only capability-discovery mechanism, because there is no nav bar.
Actions
action is the discriminator the frontend switches on. 26 distinct values:
quotes history text error
connect_wallet fallback send_token portfolio
buy_bz_token buy_sell dca tx_info
tx_history nft_info limit_order check_balance
price_alert switch_chain create_dapp contract_audit
token_safety wallet_card revoke_approvals workflow
market_intel batch autopilotDefined as BLAZ_ACTIONS in v1/models/blazMessage.js; mirrored as IAIAction in defi-dex/src/types/ai-chat.type.ts.
MODIFY_DAPP maps to create_dapp — DappPreview handles both.
Full mapping to components: action renderer.
data shape
A bag of named optional fields, one per action:
js
data: {
quotes: [Object], tradeInfo, txn, portfolio, txInfo, txHistory,
nftInfo, dapp, contract, audit, tokenSafety, walletCard,
revokeApprovals, autopilot, options, …
}Adding an intent means adding a field in two repos
v1/models/blazMessage.js's data subdocument and IChatMessage['data'] in defi-dex/src/types/ai-chat.type.ts. Miss the Mongoose side and it silently drops the field on write — the card renders empty with no error anywhere.
Notifications
Separate router, /api/v1/notifications:
| Method | Path |
|---|---|
GET | /v1/notifications |
GET | /v1/notifications/count |
POST | /v1/notifications/:id/read |
POST | /v1/notifications/read-all |
Model: models/blazNotificationModel.js. Includes the autopilot type and an expiryWarnedAt field so a 48-hour expiry warning fires once.
Feedback
| Method | Path |
|---|---|
POST | /api/v1/feedback |
Model: models/feedbackModel.js.
Training data
The prompt-refinement review pipeline. /api/v1/training:
| Method | Path | Purpose |
|---|---|---|
GET | /v1/training | List candidates |
GET | /v1/training/stats | Counts by status |
GET | /v1/training/export | Export approved examples |
PATCH | /v1/training/:id | Approve / reject one |
POST | /v1/training/bulk | Bulk update |
Model: models/trainingDataModel.js.
Chat routes on the frontend
| Route | Site |
|---|---|
/, /c/:id | AI site |
/defi/blazai, /defi/blazai/:id | Defi site (legacy) |
Use the route constants
CHAT_ROOT, chatPath(id) and isChatPath(pathname) in defi-dex/src/config/site.ts. Hardcoding /defi/blazai bounces AI-site users to defi.blazpay.com through the catch-all.
Legacy paths — do not extend
| Path | Was |
|---|---|
/api/defi/swap-ai/chat* | The previous chat implementation (controllers/ai/chats.js, models/AIChat.js, AIMessages.js) |
/api/defi/swap-ai | generateContent |
/api/defi/wit | wit.ai NLU |
/api/defi/swap-ai/chat/rasa-webhook | Rasa |
/api/defi/swap-ai/chat/webhook | Fallback webhook |
Quotas
There is no message-count limit. The only per-wallet quota in the platform is the audit one:
js
SystemSetting.getValue('audit_free_limit_per_wallet') // default 3If a chat quota is ever needed, that is the pattern to copy — see platform API.
Failure modes
| Symptom | Cause |
|---|---|
| Generic text where a card was expected | No handler for the intent (e.g. LIMIT_ORDER), or extraction returned follow_up |
| Empty sidebar for a returning user | No wallet connected — history is wallet-keyed |
| Previous wallet's history after disconnect | The clear effect did not run. See chat path |
?q= in the URL, empty composer | The effect was keyed on mount instead of location.search |
User bounced to defi.blazpay.com | A hardcoded /defi/blazai |
| Card renders empty | The data field is missing from the Mongoose schema |
| Conversation answers worse than expected | Partial conversation-provider config silently downgraded the model |
| Agent unreachable | AGENT_URL wrong, or bz-agent down on :4000 |