API Examples & Best Practices
Practical examples and best practices for working with the AGNT API.
Table of Contents
- Quick Start Examples
- Agent Examples
- Workflow Examples
- Workflow Version Control
- Goal-Based Execution
- MCP Integration
- Streaming Examples
- Error Handling
- Best Practices
Quick Start Examples
Basic Setup
// API Configuration
const REMOTE_API = 'https://api.agnt.gg';
const LOCAL_API = 'http://localhost:3333/api'; // NOTE: HTTP, not HTTPS
class AgntAPI {
constructor(token, useLocal = false) {
this.token = token;
this.baseUrl = useLocal ? LOCAL_API : REMOTE_API;
}
async request(endpoint, options = {}) {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
...options,
headers: {
Authorization: `Bearer ${this.token}`,
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Request failed');
}
return response.json();
}
async get(endpoint) {
return this.request(endpoint);
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data),
});
}
async put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data),
});
}
async delete(endpoint) {
return this.request(endpoint, {
method: 'DELETE',
});
}
}
// Usage
const api = new AgntAPI('YOUR_JWT_TOKEN');
const localApi = new AgntAPI('YOUR_JWT_TOKEN', true);Authentication
// Request magic link
async function requestMagicLink(email) {
const response = await fetch('https://api.agnt.gg/users/auth/magic-link/request', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
return await response.json();
}
// Verify magic link and get token
async function verifyMagicLink(email, code) {
const response = await fetch('https://api.agnt.gg/users/auth/magic-link/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, code }),
});
const data = await response.json();
return data.token;
}
// Use the token
await requestMagicLink('user@example.com');
// User receives code in email
const token = await verifyMagicLink('user@example.com', '123456');
const api = new AgntAPI(token);Agent Examples
Create and Execute an Agent
// 1. Create an agent
const agent = await api.post('/agents/save', {
name: 'Research Assistant',
description: 'Helps with research tasks',
model: 'gpt-4',
systemPrompt: `You are a research assistant. Your role is to:
- Find accurate information from reliable sources
- Summarize findings clearly
- Cite all sources
- Ask clarifying questions when needed`,
temperature: 0.7,
maxTokens: 2000,
tools: ['web_search', 'web_scraper'],
metadata: {
category: 'research',
tags: ['academic', 'analysis'],
},
});
console.log('Agent created:', agent.agent.id);
// 2. Chat with the agent (Local API)
const localApi = new AgntAPI(token, true);
const response = await localApi.post(`/agents/${agent.agent.id}/chat`, {
message: 'What are the latest developments in quantum computing?',
conversationHistory: [],
stream: false,
});
console.log('Response:', response.response);Streaming Chat with Agent
async function streamChat(agentId, message) {
const formData = new FormData();
formData.append('message', message);
formData.append('stream', 'true');
const response = await fetch(`http://localhost:3333/api/agents/${agentId}/chat-stream`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
},
body: formData,
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
if (data.type === 'token') {
process.stdout.write(data.content);
} else if (data.type === 'complete') {
console.log('\n\nCompleted!');
console.log('Tokens used:', data.tokensUsed);
}
}
}
}
}
// Usage
await streamChat('agent-123', 'Explain quantum entanglement');Agent with File Upload
async function chatWithFiles(agentId, message, files) {
const formData = new FormData();
formData.append('message', message);
// Add multiple files
files.forEach((file) => {
formData.append('files', file, file.name);
});
const response = await fetch(`http://localhost:3333/api/orchestrator/agent-chat`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
},
body: formData,
});
return response.json();
}
// Usage
const files = [new File(['content'], 'document.txt'), new File(['data'], 'data.csv')];
const result = await chatWithFiles('agent-123', 'Analyze these files', files);Workflow Examples
Important Concepts:
POST /:id/startactivates a workflow (turns it on to listen for triggers like timers/webhooks). It does not execute it with runtime inputs.POST /:id/stopdeactivates a workflow (turns it off). It does not cancel a running execution.GET /:id/statusreturns the workflow's activation state (active,inactive, orerror), not execution progress. There is noprogresspercentage field.
Complete Workflow Route Reference
| Method | Endpoint | Description |
|---|---|---|
GET |
/workflows/health |
Health check (no auth) |
GET |
/workflows/ |
Get all workflows |
GET |
/workflows/summary |
Get lightweight workflow summaries |
POST |
/workflows/save |
Create/save a workflow |
POST |
/workflows/analyze-dependencies |
Analyze node dependencies |
GET |
/workflows/:id |
Get workflow by ID |
PUT |
/workflows/:id |
Update a workflow |
DELETE |
/workflows/:id |
Delete a workflow |
PUT |
/workflows/:id/name |
Rename a workflow |
GET |
/workflows/:id/status |
Get workflow activation state |
POST |
/workflows/:id/start |
Activate workflow |
POST |
/workflows/:id/stop |
Deactivate workflow |
GET |
/workflows/:id/versions |
List version history |
GET |
/workflows/:id/versions/:versionId |
Get specific version |
POST |
/workflows/:id/revert |
Revert to a version |
POST |
/workflows/:id/checkpoint |
Create a named checkpoint |
GET |
/workflows/:id/versions/compare |
Compare two versions |
GET |
/workflows/:id/versions/stats |
Version storage statistics |
Create and Activate a Workflow
// 1. Create a workflow
const result = await api.post('/workflows/save', {
name: 'Content Creation Pipeline',
description: 'Automated blog post creation',
nodes: [
{
id: 'research',
type: 'agent',
config: {
agentId: 'research-agent-id',
prompt: 'Research {{topic}}',
},
},
{
id: 'write',
type: 'agent',
config: {
agentId: 'writer-agent-id',
prompt: 'Write a blog post about: {{research.output}}',
},
},
{
id: 'edit',
type: 'agent',
config: {
agentId: 'editor-agent-id',
prompt: 'Edit and improve: {{write.output}}',
},
},
],
edges: [
{ source: 'research', target: 'write' },
{ source: 'write', target: 'edit' },
],
});
const workflowId = result.workflow.id;
console.log('Workflow created:', workflowId);
// 2. Activate the workflow (starts listening for triggers)
// NOTE: This does NOT accept runtime inputs β it toggles the workflow ON
const activation = await localApi.post(`/workflows/${workflowId}/start`);
console.log(activation.message); // "Workflow activated successfully"
// 3. Check workflow activation state
const status = await localApi.get(`/workflows/${workflowId}/status`);
console.log(`Status: ${status.status}`); // "active", "inactive", or "error"
console.log(`Executions: ${status.executionCount}`);
console.log(`Errors: ${status.errorCount}`);
// NOTE: There is NO status.progress fieldGet Workflow Summaries
// Get lightweight list (faster than fetching full workflow data)
const summaries = await localApi.get('/workflows/summary');
summaries.forEach((wf) => {
console.log(`${wf.name} β ${wf.status} (created: ${wf.created_at})`);
});Update and Rename Workflows
// Full update (replaces nodes, edges, config, etc.)
const updated = await localApi.put(`/workflows/${workflowId}`, {
name: 'Updated Pipeline',
description: 'Improved content pipeline',
nodes: [
/* ... updated nodes ... */
],
edges: [
/* ... updated edges ... */
],
config: {},
});
console.log('Updated:', updated.workflow.name);
// Quick rename (only changes the name)
const renamed = await localApi.put(`/workflows/${workflowId}/name`, {
name: 'My Awesome Pipeline',
});
console.log('Renamed to:', renamed.workflow.name);Analyze Workflow Dependencies
// Analyze node dependencies before saving
const analysis = await localApi.post('/workflows/analyze-dependencies', {
nodes: [
{ id: 'fetch', type: 'api', config: { url: 'https://api.example.com/data' } },
{ id: 'process', type: 'code', config: { input: '{{fetch.output}}' } },
{ id: 'notify', type: 'email', config: { body: '{{process.result}}' } },
],
edges: [
{ source: 'fetch', target: 'process' },
{ source: 'process', target: 'notify' },
],
});
console.log('Dependencies:', analysis);Deactivate and Delete Workflows
// Deactivate a workflow (stops listening for triggers)
// NOTE: This does NOT "cancel" a running execution
await localApi.post(`/workflows/${workflowId}/stop`);
console.log('Workflow deactivated');
// Delete workflow entirely
await localApi.delete(`/workflows/${workflowId}`);
console.log('Workflow deleted');Workflow with Error Handling
async function manageWorkflowSafely(workflowId) {
try {
// Activate workflow
const activation = await localApi.post(`/workflows/${workflowId}/start`);
console.log(activation.message);
// Check state
const status = await localApi.get(`/workflows/${workflowId}/status`);
if (status.status === 'error') {
console.error('Workflow entered error state');
// Deactivate the workflow
await localApi.post(`/workflows/${workflowId}/stop`);
return { success: false, error: 'Workflow in error state' };
}
return {
success: true,
status: status.status,
executionCount: status.executionCount,
};
} catch (error) {
console.error('Workflow error:', error);
// Deactivate workflow if something went wrong
try {
await localApi.post(`/workflows/${workflowId}/stop`);
} catch (stopError) {
console.error('Failed to deactivate workflow:', stopError);
}
return {
success: false,
error: error.message,
};
}
}Workflow Version Control
These endpoints manage workflow version history, allowing you to create checkpoints, compare versions, and revert to previous states.
Create a Checkpoint and Manage Versions
// 1. Create a named checkpoint before making changes
const checkpoint = await localApi.post(`/workflows/${workflowId}/checkpoint`, {
name: 'Before refactor',
currentWorkflowState: {
// The full workflow state object (nodes, edges, config, etc.)
nodes: [
/* current nodes */
],
edges: [
/* current edges */
],
},
});
console.log(`Checkpoint created: v${checkpoint.versionNumber} β "${checkpoint.checkpointName}"`);
// 2. List version history
const history = await localApi.get(`/workflows/${workflowId}/versions?limit=10&offset=0`);
history.versions.forEach((v) => {
const tag = v.is_checkpoint ? ` [π ${v.checkpoint_name}]` : '';
console.log(` v${v.version_number} β ${v.change_description}${tag} (${v.created_at})`);
});
// 3. List only checkpoints
const checkpoints = await localApi.get(`/workflows/${workflowId}/versions?checkpointsOnly=true`);
console.log(`Found ${checkpoints.versions.length} checkpoints`);Compare and Revert Versions
// 4. Compare two versions
const comparison = await localApi.get(`/workflows/${workflowId}/versions/compare?versionA=1&versionB=5`);
console.log('Nodes added:', comparison.diff.nodesAdded.length);
console.log('Nodes removed:', comparison.diff.nodesRemoved.length);
console.log('Nodes modified:', comparison.diff.nodesModified.length);
console.log('Edges added:', comparison.diff.edgesAdded.length);
console.log('Edges removed:', comparison.diff.edgesRemoved.length);
// 5. Get a specific version's full data
const version = await localApi.get(`/workflows/${workflowId}/versions/3`);
console.log('Version state:', version.version.workflow_state);
// 6. Revert to a previous version
const revert = await localApi.post(`/workflows/${workflowId}/revert`, {
versionId: 3,
});
console.log(`Reverted to v${revert.revertedToVersion}`);
// Note: This also broadcasts 'workflow:reverted' via WebSocketVersion Storage Statistics
// Check how much storage versions are using
const stats = await localApi.get(`/workflows/${workflowId}/versions/stats`);
console.log(`Total versions: ${stats.stats.totalVersions}`);
console.log(`Checkpoints: ${stats.stats.checkpoints}`);
console.log(`Storage used: ${(stats.stats.totalSizeBytes / 1024).toFixed(1)} KB`);
console.log(`Oldest: ${stats.stats.oldestVersion}`);
console.log(`Newest: ${stats.stats.newestVersion}`);Goal-Based Execution
Create and Execute Goal
// 1. Create a goal
const goal = await localApi.post('/goals/create', {
description: 'Research and create a comprehensive report on renewable energy trends',
agentId: 'research-agent-id',
successCriteria: [
'Include data from at least 5 reliable sources',
'Cover solar, wind, and hydro energy',
'Provide cost analysis',
'Include future predictions',
],
maxIterations: 15,
});
// 2. Execute the goal
await localApi.post(`/goals/${goal.goalId}/execute`);
// 3. Monitor progress
async function monitorGoal(goalId) {
while (true) {
const status = await localApi.get(`/goals/${goalId}/status`);
console.log(`
Status: ${status.status}
Progress: ${status.progress}%
Current Step: ${status.currentStep}
Iterations: ${status.iterations}/${status.maxIterations}
`);
if (status.status === 'completed') {
return status;
} else if (status.status === 'failed') {
throw new Error('Goal execution failed');
}
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}
const result = await monitorGoal(goal.goalId);
// 4. Evaluate the result
const evaluation = await localApi.post(`/goals/${goal.goalId}/evaluate`, {
criteria: ['accuracy', 'completeness', 'clarity', 'sources'],
compareToGoldenStandard: false,
});
console.log('Evaluation:', evaluation);
// 5. Save as golden standard if good
if (evaluation.score >= 85) {
await localApi.post(`/goals/${goal.goalId}/golden-standard`);
console.log('Saved as golden standard!');
}MCP Integration
Add and Use MCP Server
// 1. Search for MCP servers
const packages = await localApi.get('/npm/search?q=weather');
console.log('Available packages:', packages.packages);
// 2. Add MCP server
const server = await localApi.post('/mcp/servers', {
name: 'weather-server',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-weather'],
env: {},
});
// 3. Test connection
const test = await localApi.post('/mcp/servers/weather-server/test');
console.log('Connection test:', test);
// 4. Get capabilities
const capabilities = await localApi.get('/mcp/servers/weather-server/capabilities');
console.log('Available tools:', capabilities.tools);
// 5. Use MCP tools in agent
const agent = await api.post('/agents/save', {
name: 'Weather Agent',
model: 'gpt-4',
systemPrompt: 'You provide weather information using the weather tools.',
tools: ['mcp:weather-server:get_weather', 'mcp:weather-server:get_forecast'],
});MCP Server Management
class MCPManager {
constructor(api) {
this.api = api;
}
async listServers() {
const { servers } = await this.api.get('/mcp/servers');
return servers;
}
async addServer(name, command, args, env = {}) {
return this.api.post('/mcp/servers', {
name,
command,
args,
env,
});
}
async removeServer(name) {
return this.api.delete(`/mcp/servers/${name}`);
}
async testServer(name) {
return this.api.post(`/mcp/servers/${name}/test`);
}
async getCapabilities(name) {
return this.api.get(`/mcp/servers/${name}/capabilities`);
}
async healthCheck() {
const servers = await this.listServers();
const results = [];
for (const server of servers) {
try {
const test = await this.testServer(server.name);
results.push({
name: server.name,
status: test.success ? 'healthy' : 'unhealthy',
latency: test.latency,
});
} catch (error) {
results.push({
name: server.name,
status: 'error',
error: error.message,
});
}
}
return results;
}
}
// Usage
const mcpManager = new MCPManager(localApi);
const health = await mcpManager.healthCheck();
console.log('MCP Health:', health);Streaming Examples
Server-Sent Events Handler
class SSEHandler {
constructor(url, token) {
this.url = url;
this.token = token;
this.eventSource = null;
}
connect(onMessage, onError) {
const urlWithToken = `${this.url}?token=${this.token}`;
this.eventSource = new EventSource(urlWithToken);
this.eventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
onMessage(data);
} catch (error) {
console.error('Failed to parse SSE data:', error);
}
};
this.eventSource.onerror = (error) => {
console.error('SSE error:', error);
if (onError) onError(error);
this.disconnect();
};
}
disconnect() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
}
}
// Usage: Connection Health Monitoring
const healthStream = new SSEHandler('http://localhost:3333/api/users/connection-health-stream', token);
healthStream.connect(
(data) => {
console.log('Health update:', data);
},
(error) => {
console.error('Stream error:', error);
},
);
// Disconnect when done
// healthStream.disconnect();Error Handling
Comprehensive Error Handler
class APIError extends Error {
constructor(message, statusCode, details) {
super(message);
this.name = 'APIError';
this.statusCode = statusCode;
this.details = details;
}
}
async function safeRequest(url, options = {}) {
try {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
throw new APIError(error.error || 'Request failed', response.status, error.details);
}
return await response.json();
} catch (error) {
if (error instanceof APIError) {
throw error;
}
// Network or parsing error
throw new APIError('Network error or invalid response', 0, error.message);
}
}
// Usage with retry logic
async function requestWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await safeRequest(url, options);
} catch (error) {
if (error.statusCode === 429) {
// Rate limited, wait and retry
const waitTime = Math.pow(2, i) * 1000;
console.log(`Rate limited, waiting ${waitTime}ms...`);
await new Promise((resolve) => setTimeout(resolve, waitTime));
continue;
}
if (error.statusCode >= 500 && i < maxRetries - 1) {
// Server error, retry
console.log(`Server error, retrying (${i + 1}/${maxRetries})...`);
await new Promise((resolve) => setTimeout(resolve, 1000));
continue;
}
// Don't retry for client errors or final attempt
throw error;
}
}
}Best Practices
1. Token Management
class TokenManager {
constructor() {
this.token = null;
this.expiresAt = null;
}
setToken(token, expiresIn = 30 * 24 * 60 * 60 * 1000) {
this.token = token;
this.expiresAt = Date.now() + expiresIn;
localStorage.setItem('agnt_token', token);
localStorage.setItem('agnt_token_expires', this.expiresAt.toString());
}
getToken() {
if (!this.token) {
this.token = localStorage.getItem('agnt_token');
this.expiresAt = parseInt(localStorage.getItem('agnt_token_expires') || '0');
}
if (this.isExpired()) {
this.clearToken();
return null;
}
return this.token;
}
isExpired() {
return this.expiresAt && Date.now() >= this.expiresAt;
}
clearToken() {
this.token = null;
this.expiresAt = null;
localStorage.removeItem('agnt_token');
localStorage.removeItem('agnt_token_expires');
}
}2. Rate Limiting
class RateLimiter {
constructor(maxRequests, windowMs) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
this.requests = [];
}
async acquire() {
const now = Date.now();
this.requests = this.requests.filter((time) => now - time < this.windowMs);
if (this.requests.length >= this.maxRequests) {
const oldestRequest = this.requests[0];
const waitTime = this.windowMs - (now - oldestRequest);
await new Promise((resolve) => setTimeout(resolve, waitTime));
return this.acquire();
}
this.requests.push(now);
}
}
// Usage
const limiter = new RateLimiter(100, 60 * 60 * 1000); // 100 requests per hour
async function rateLimitedRequest(url, options) {
await limiter.acquire();
return fetch(url, options);
}3. Caching
class APICache {
constructor(ttl = 5 * 60 * 1000) {
this.cache = new Map();
this.ttl = ttl;
}
set(key, value) {
this.cache.set(key, {
value,
expires: Date.now() + this.ttl,
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() > item.expires) {
this.cache.delete(key);
return null;
}
return item.value;
}
clear() {
this.cache.clear();
}
}
// Usage
const cache = new APICache();
async function cachedRequest(url, options) {
const cacheKey = `${url}:${JSON.stringify(options)}`;
const cached = cache.get(cacheKey);
if (cached) {
console.log('Cache hit:', cacheKey);
return cached;
}
const response = await fetch(url, options);
const data = await response.json();
cache.set(cacheKey, data);
return data;
}4. Batch Operations
async function batchCreateAgents(agents) {
const results = [];
const errors = [];
for (const agentData of agents) {
try {
const agent = await api.post('/agents/save', agentData);
results.push(agent);
} catch (error) {
errors.push({
agent: agentData.name,
error: error.message,
});
}
}
return { results, errors };
}
// Usage
const agentsToCreate = [
{ name: 'Agent 1', model: 'gpt-4', systemPrompt: '...' },
{ name: 'Agent 2', model: 'gpt-4', systemPrompt: '...' },
{ name: 'Agent 3', model: 'gpt-4', systemPrompt: '...' },
];
const { results, errors } = await batchCreateAgents(agentsToCreate);
console.log(`Created ${results.length} agents, ${errors.length} errors`);5. Logging and Monitoring
class APILogger {
constructor() {
this.logs = [];
}
log(level, message, data = {}) {
const entry = {
timestamp: new Date().toISOString(),
level,
message,
data,
};
this.logs.push(entry);
console.log(`[${level}] ${message}`, data);
// Send to monitoring service
this.sendToMonitoring(entry);
}
async sendToMonitoring(entry) {
// Implement your monitoring service integration
// e.g., send to Sentry, LogRocket, etc.
}
getLogs(level = null) {
if (level) {
return this.logs.filter((log) => log.level === level);
}
return this.logs;
}
}
const logger = new APILogger();
// Usage
try {
const result = await api.post('/agents/save', agentData);
logger.log('info', 'Agent created successfully', { agentId: result.agent.id });
} catch (error) {
logger.log('error', 'Failed to create agent', { error: error.message });
}Next Steps
- Getting Started - API basics
- Authentication - Auth methods
- Remote API Endpoints - Cloud endpoints
- Remote API Overview - Remote API features
- Local API Endpoints - Desktop endpoints
- Local API Overview - Local API features