Conversational Token Assistant

Build a chatbot that lets users query, transfer, and manage their tokens through natural language.

What You'll Build

Most users don't want to learn API endpoints, they want to talk to their tokens. In this tutorial you'll build a conversational assistant that can look up tokens, check balances, transfer objects, and answer questions about a user's collection, all through chat. We'll use tool-calling (function calling) so the LLM can invoke Dual APIs on behalf of the user.

Step 1, Define the Tool Schema

Create the tool definitions that tell the LLM what Dual operations are available:

javascript
const tools = [
{
type: 'function',
function: {
name: 'list_my_tokens',
description: 'List tokens owned by the current user, optionally filtered by template',
parameters: {
type: 'object',
properties: {
template_name: {
type: 'string',
description: 'Filter by template name (optional)'
},
limit: {
type: 'number',
description: 'Max results (default 10)'
}
}
}
}
},
{
type: 'function',
function: {
name: 'get_token_details',
description: 'Get full details of a specific token by ID',
parameters: {
type: 'object',
properties: {
object_id: { type: 'string', description: 'The token/object ID' }
},
required: ['object_id']
}
}
},
{
type: 'function',
function: {
name: 'transfer_token',
description: 'Transfer a token to another wallet',
parameters: {
type: 'object',
properties: {
object_id: { type: 'string', description: 'Token to transfer' },
recipient: { type: 'string', description: 'Recipient wallet or email' }
},
required: ['object_id', 'recipient']
}
}
},
{
type: 'function',
function: {
name: 'search_tokens',
description: 'Search tokens by properties like points, tier, or tags',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Natural language search query' },
filters: { type: 'object', description: 'Property filters' }
},
required: ['query']
}
}
}
];

Step 2, Implement Tool Handlers

Each tool maps to a Dual API call:

javascript
const API = 'https://api-testnet.dual.network';
const toolHandlers = {
async list_my_tokens({ template_name, limit = 10 }) {
const params = new URLSearchParams({ limit });
if (template_name) params.set('template_name', template_name);
const res = await dualFetch(\"/objects?\" + params + "\");
return res.results.map(o => ({
id: o.id, name: o.template_name,
properties: o.properties
}));
},
async get_token_details({ object_id }) {
return dualFetch(\"/objects/\" + object_id + "\");
},
async transfer_token({ object_id, recipient }) {
return dualFetch('/ebus/actions', {
method: 'POST',
body: JSON.stringify({
action_name: 'Transfer',
object_id, new_owner: recipient
})
});
},
async search_tokens({ query, filters }) {
return dualFetch('/objects/search', {
method: 'POST',
body: JSON.stringify({ filters: filters || {}, limit: 20 })
});
}
};

Step 3, Build the Chat Loop

Create the conversation loop that sends messages, handles tool calls, and streams responses:

javascript
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic();
async function chat(userMessage, history = []) {
history.push({ role: 'user', content: userMessage });
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-5-20250514',
max_tokens: 1024,
system: \"You are a helpful assistant for managing DUAL tokens.
The user may ask about their tokens, request transfers, or search
their collection. Use the available tools to fulfill requests.
Always confirm before executing transfers.\",
tools,
messages: history
});
// Handle tool use
if (response.stop_reason === 'tool_use') {
const toolUse = response.content.find(b => b.type === 'tool_use');
const result = await toolHandlers[toolUse.name](toolUse.input);
history.push({ role: 'assistant', content: response.content });
history.push({
role: 'user',
content: [{
type: 'tool_result',
tool_use_id: toolUse.id,
content: JSON.stringify(result)
}]
});
return chat('', history); // Continue the conversation
}
return response.content[0].text;
}

Step 4, Example Conversations

Here's what the assistant can handle:

text
User: What tokens do I have?
Bot: [calls list_my_tokens] You have 12 tokens across 3 templates:
- 5x Coffee Stamp Cards (2 fully stamped)
- 4x Event Passes (upcoming concert, 2 expired)
- 3x Loyalty Badges (gold tier)
User: Transfer my gold badge to alice@example.com
Bot: I can transfer your Gold Loyalty Badge (ID: obj_789) to
alice@example.com. This action cannot be undone. Proceed?
User: Yes
Bot: [calls transfer_token] Done! The Gold Loyalty Badge has been
transferred to alice@example.com.

Safety Pattern: Always confirm destructive or irreversible actions (transfers, burns, deletes) before executing them. The assistant should summarize what it's about to do and wait for explicit confirmation.

Companion Repo: Get the full working source code for this tutorial at github.com/orgs/DualOrg/dual-ai-chatbot, clone it, add your API keys, and run it locally in minutes.

What's Next?

Add intelligence to your workflows with AI-Powered Webhook Automation, let AI decide how to handle incoming events.