Skip to content

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 ​

MethodPathPurpose
POST/v1/blaz/message/streamSend a message, receive an SSE stream
POST/v1/blaz/messageSame, non-streaming JSON
GET/v1/blaz/chat/:chatIdOne conversation's messages
GET/v1/blaz/sessionsThe wallet's chat list
GET/v1/blaz/suggestionsSuggestion chips
GET/v1/blaz/home-chipsLanding-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 ​

FieldNotes
messageThe user's text
walletIdentity and history key
chatIdExisting session; omit to start a new one
chainIdThe wallet's current chain, passed to the agent
currencyDisplay currency for portfolio and price answers

Response — SSE ​

Three named events, in this order:

EventPayloadWhy
meta{ action, data }First, so the card renders before any prose arrives
tokenA text chunkRepeated
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 ​

  1. Find or create a blazSession for the wallet.
  2. Persist the user's blazMessage.
  3. getAgentResponse(...) → POST {AGENT_URL}/chat/:sessionId with { input, chain, supported_chains, activeDappProjectId? } → { intent, data, text }.
  4. Switch on intent, call the handler, get { text, data, action }.
  5. Persist the bot's blazMessage.
  6. 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             autopilot

Defined 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:

MethodPath
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 ​

MethodPath
POST/api/v1/feedback

Model: models/feedbackModel.js.

Training data ​

The prompt-refinement review pipeline. /api/v1/training:

MethodPathPurpose
GET/v1/trainingList candidates
GET/v1/training/statsCounts by status
GET/v1/training/exportExport approved examples
PATCH/v1/training/:idApprove / reject one
POST/v1/training/bulkBulk update

Model: models/trainingDataModel.js.

Chat routes on the frontend ​

RouteSite
/, /c/:idAI site
/defi/blazai, /defi/blazai/:idDefi 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 ​

PathWas
/api/defi/swap-ai/chat*The previous chat implementation (controllers/ai/chats.js, models/AIChat.js, AIMessages.js)
/api/defi/swap-aigenerateContent
/api/defi/witwit.ai NLU
/api/defi/swap-ai/chat/rasa-webhookRasa
/api/defi/swap-ai/chat/webhookFallback 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 3

If a chat quota is ever needed, that is the pattern to copy — see platform API.

Failure modes ​

SymptomCause
Generic text where a card was expectedNo handler for the intent (e.g. LIMIT_ORDER), or extraction returned follow_up
Empty sidebar for a returning userNo wallet connected — history is wallet-keyed
Previous wallet's history after disconnectThe clear effect did not run. See chat path
?q= in the URL, empty composerThe effect was keyed on mount instead of location.search
User bounced to defi.blazpay.comA hardcoded /defi/blazai
Card renders emptyThe data field is missing from the Mongoose schema
Conversation answers worse than expectedPartial conversation-provider config silently downgraded the model
Agent unreachableAGENT_URL wrong, or bz-agent down on :4000

Internal + developer documentation. Usage figures are point-in-time snapshots — re-verify before publishing them.