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
doneStream 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_dappDappPreview 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.