API Reference
AI Agent

AI Agent Routes

Base URL: https://agent.insiders.bot
Env var: VITE_AGENT_API_URL
Frontend client: src/components/AiAgent/agentApi.ts
Server code: not available locally — deployed separately

The agent uses SSE (Server-Sent Events) for streaming responses — POST /api/agent/chat returns text/event-stream, not JSON. Auth is the same user JWT (Authorization: Bearer <token>) as the main API.


Chat · /api/agent/chat

MethodPathAuthDescription
POST/api/agent/chat🔒Send a message, stream response via SSE

Request body

{
  conversation_id: string        // UUID — generate with newConversationId()
  message: string
  byok_provider?: "claude" | "deepseek" | null
  proposed_action?: ProposedAction | null  // set when user confirms a recommendation
}

ProposedAction shape

{
  tool: "place_order" | "close_position" | "register_copy_trade"
  condition_id?: string
  side?: "YES" | "NO"
  usd_amount?: number        // for place_order
  fraction?: number          // for close_position (1.0 = full close)
  target_wallet?: string     // for register_copy_trade
  market_title?: string
  market_slug?: string
}

SSE event stream

Each event is event: <type>\ndata: <json>\n\n. Events in order:

EventDataDescription
conversation{ conversation_id }Echoes the conversation ID
thinking{ text }Agent's internal reasoning (shown as thinking indicator)
token{ text }Streaming response token
tool_call{ tool, args, call_id }Agent called a tool
tool_progress{ call_id, phase, text, tool?, stage_id? }Tool running / success / error
tool_result{ call_id, tool?, summary, elapsed_ms, error?, error_user_msg? }Tool finished
entities_updateRecord<string, unknown>Market/wallet entities extracted from context
done{ recommendation?, inspector? }Final event — contains action recommendation if any
error{ message, error_code? }Stream-level error

done — recommendation types

The agent may emit a recommendation on the final done event. The frontend renders this as an action card.

trade — buy or close a position:

{
  type: "trade"
  trade: {
    action?: "place_order" | "close_position"
    condition_id: string
    side: "YES" | "NO"
    suggested_usd?: number
    usd_amount?: number
    fraction?: number          // for close_position
    market_title?: string
    current_price?: number
  }
}

redirect — navigate to alternative markets/wallets:

{
  type: "redirect"
  redirect?: {
    alternatives: Array<{
      condition_id?: string
      title?: string
      slug?: string
      kind?: "market" | "wallet"
      label?: string
      ref?: string
    }>
  }
}

watch — add market to watchlist:

{ type: "watch"; market: { condition_id: string; title: string } }

no_op — no action:

{ type: "no_op"; reasoning?: string }

Cancel turn · /api/agent/conversations/:id/turns/:index/cancel

MethodPathAuthDescription
POST/api/agent/conversations/:conversationId/turns/:turnIndex/cancel🔒Abort an in-progress agent turn

Conversation history

MethodPathAuthDescription
GET/api/agent/conversations🔒List conversations ({ conversations: ConversationSummary[] })
GET/api/agent/conversations/:id🔒Full conversation with messages + events
DELETE/api/agent/conversations/:id🔒Delete a conversation

ConversationSummary

{
  conversation_id: string
  last_at: string        // ISO timestamp
  msg_count: number
  title: string
}

Trade confirmation flow

When the user clicks Confirm on a recommendation card, the frontend:

  1. Calls synthesizeConfirmationMessage(rec) to build a plain-English message
  2. Sends a new POST /api/agent/chat with the message and proposed_action
  3. The agent's intent_completeness guard re-validates that the message names every disambiguator (side, amount/fraction, market reference)

This contract is mirrored in polyinsiders-backend-v1/Tgbot/services/bot.ts → synthesizeConfirmationMessage. If you change one, change both.