API Examples & Best Practices

Practical examples and best practices for working with the AGNT API.

Table of Contents


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/start activates a workflow (turns it on to listen for triggers like timers/webhooks). It does not execute it with runtime inputs.
  • POST /:id/stop deactivates a workflow (turns it off). It does not cancel a running execution.
  • GET /:id/status returns the workflow's activation state (active, inactive, or error), not execution progress. There is no progress percentage 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 field

Get 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 WebSocket

Version 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