Skip to main content
This example builds a small inbox-style app with the same task/thread model exposed by the v1 REST API:
In this model, the returned task id is also the thread ID. Create a new thread by omitting id. Reply in an existing thread by sending the previous task id back as id.

What This App Does

  1. Loads all agents.
  2. Loads the selected agent’s task list as thread rows.
  3. Loads the selected thread’s full root conversation.
  4. Sends a new message without id to create a brand-new thread.
  5. Sends a message with id to continue an existing thread.

Prerequisites

  • Node.js 18 or newer
  • An XPANDER_API_KEY
  • Optionally XPANDER_BASE_URL if you are not using https://api.xpander.ai

Project Structure

1. package.json

package.json

2. server.js

This tiny backend keeps your API key on the server and exposes four browser-safe routes:
  • GET /api/agents
  • GET /api/agents/:agentId/threads
  • GET /api/tasks/:taskId/messages
  • POST /api/agents/:agentId/messages
server.js

3. public/index.html

The frontend is plain HTML, CSS, and browser JavaScript. It renders:
  • an agent list
  • a thread list for the selected agent
  • a message history panel
  • a composer that either creates a new thread or replies in the selected thread
public/index.html

4. Run the App

Open http://localhost:3000.

How to Use It

  1. Pick an agent in the left column.
  2. Click a thread in the middle column to load its history.
  3. Click Start New Thread and send a message to create a new conversation.
  4. Select an existing thread and send another message to continue it.

Why This Matches the API

  • The agent list comes from GET /v1/agents.
  • The thread list comes from GET /v1/agents/{agent_id}/tasks.
  • Each thread row is keyed by task.id.
  • The message history for that thread comes from GET /v1/tasks/{task_id}/thread.
  • Sending a new message without id creates a new thread.
  • Sending a message with id appends to the existing thread and keeps using that same task.id.

Streaming Upgrade

This example uses Invoke Agent (Sync) because it is the smallest complete app. If you want live partial output, replace the send route with Invoke Agent (Stream). The thread model stays the same:
  • omit id to create a new thread
  • include id to continue an existing thread
  • refresh GET /v1/tasks/{task_id}/thread after task_finished if you want the persisted message history

Production Notes

  • Keep XPANDER_API_KEY on the server, never in browser JavaScript
  • Paginate /v1/agents and /v1/agents/{agent_id}/tasks for large workspaces
  • Use /thread/full instead of /thread if you need sub-task visibility for multi-agent runs