Build/APIs

Calling Agents from Routes

Import and invoke agents from your routes

Routes import agents directly and call them using agent.run(). Use agent.validator() for type-safe request validation.

Basic Agent Call

Import an agent and call it in your route:

import { createRouter } from '@agentuity/runtime';
import chat from '@agent/chat';
 
const router = createRouter();
 
router.post('/chat', async (c) => {
  const { message } = await c.req.json();
  const result = await chat.run({ message });
  return c.json(result);
});
 
export default router;

The agent receives validated input (if it has a schema) and returns typed output.

With Validation

Use agent.validator() for type-safe request validation:

import { createRouter } from '@agentuity/runtime';
import chat from '@agent/chat';
 
const router = createRouter();
 
router.post('/chat', chat.validator(), async (c) => {
  const data = c.req.valid('json'); // Fully typed from agent schema
  const result = await chat.run(data);
  return c.json(result);
});
 
export default router;

Multiple Agents

Import and use multiple agents in the same route file:

import { createRouter } from '@agentuity/runtime';
import chat from '@agent/chat';
import summarizer from '@agent/summarizer';
import teamMembers from '@agent/team/members';
 
const router = createRouter();
 
router.post('/process', async (c) => {
  const input = await c.req.json();
 
  // Call different agents
  const chatResult = await chat.run({ message: input.text });
  const summary = await summarizer.run({ content: input.text });
  const members = await teamMembers.run({ teamId: input.teamId });
 
  return c.json({ chatResult, summary, members });
});
 
export default router;

Parallel Agent Calls

Run multiple agents concurrently when they don't depend on each other:

import { createRouter } from '@agentuity/runtime';
import sentimentAnalyzer from '@agent/sentiment-analyzer';
import topicExtractor from '@agent/topic-extractor';
import summarizer from '@agent/summarizer';
 
const router = createRouter();
 
router.post('/analyze', async (c) => {
  const { content } = await c.req.json();
 
  // Run agents in parallel
  const [sentiment, topics, summary] = await Promise.all([
    sentimentAnalyzer.run({ text: content }),
    topicExtractor.run({ text: content }),
    summarizer.run({ content }),
  ]);
 
  return c.json({ sentiment, topics, summary });
});
 
export default router;

Background Agent Calls

Use c.waitUntil() to run agents after responding to the client:

import { createRouter } from '@agentuity/runtime';
import webhookProcessor from '@agent/webhook-processor';
 
const router = createRouter();
 
router.post('/webhook', async (c) => {
  const payload = await c.req.json();
 
  // Acknowledge immediately
  c.waitUntil(async () => {
    // Process in background
    await webhookProcessor.run(payload);
    c.var.logger.info('Webhook processed');
  });
 
  return c.json({ received: true });
});
 
export default router;

Webhook Best Practice

Webhook providers expect fast responses (usually under 3 seconds). Use c.waitUntil() to acknowledge receipt immediately and process the payload in the background.

Error Handling

Wrap agent calls in try-catch for graceful error handling:

import { createRouter } from '@agentuity/runtime';
import chat from '@agent/chat';
 
const router = createRouter();
 
router.post('/safe-chat', async (c) => {
  const { message } = await c.req.json();
 
  try {
    const result = await chat.run({ message });
    return c.json({ success: true, result });
  } catch (error) {
    c.var.logger.error('Agent call failed', {
      agent: 'chat',
      error: error instanceof Error ? error.message : String(error),
    });
 
    return c.json(
      { success: false, error: 'Chat processing failed' },
      500
    );
  }
});
 
export default router;

Full Example: Multi-Endpoint API

Combine authentication, validation, and multiple agent calls:

import { createRouter } from '@agentuity/runtime';
import { createMiddleware } from 'hono/factory';
import { s } from '@agentuity/schema';
import chat from '@agent/chat';
import summarizer from '@agent/summarizer';
import sentimentAnalyzer from '@agent/sentiment-analyzer';
import entityExtractor from '@agent/entity-extractor';
 
const router = createRouter();
 
// Auth middleware
const authMiddleware = createMiddleware(async (c, next) => {
  const apiKey = c.req.header('X-API-Key');
  if (!apiKey) {
    return c.json({ error: 'API key required' }, 401);
  }
 
  const keyData = await c.var.kv.get('api-keys', apiKey);
  if (!keyData.exists) {
    return c.json({ error: 'Invalid API key' }, 401);
  }
 
  c.set('userId', keyData.data.userId);
  await next();
});
 
// Apply auth to all routes
router.use('/*', authMiddleware);
 
// Chat endpoint - uses chat agent's schema
router.post('/chat', chat.validator(), async (c) => {
  const userId = c.var.userId;
  const data = c.req.valid('json');
 
  const result = await chat.run({
    ...data,
    userId,
  });
 
  // Track usage in background
  c.waitUntil(async () => {
    await c.var.kv.set('usage', `${userId}:${Date.now()}`, {
      endpoint: 'chat',
      tokens: result.tokensUsed,
    });
  });
 
  return c.json(result);
});
 
// Summarization endpoint - uses summarizer's schema
router.post('/summarize', summarizer.validator(), async (c) => {
  const data = c.req.valid('json');
  const result = await summarizer.run(data);
  return c.json(result);
});
 
// Multi-agent analysis - uses custom schema
router.post('/analyze',
  sentimentAnalyzer.validator({
    input: s.object({ content: s.string() }),
  }),
  async (c) => {
    const { content } = c.req.valid('json');
 
    // Run multiple agents in parallel
    const [sentiment, entities, summary] = await Promise.all([
      sentimentAnalyzer.run({ text: content }),
      entityExtractor.run({ text: content }),
      summarizer.run({ content, maxLength: 100 }),
    ]);
 
    return c.json({
      sentiment: sentiment.score,
      entities: entities.items,
      summary: summary.text,
    });
  }
);
 
export default router;

Validator Pattern

Import the agent you're calling and use agent.validator() for its schema, or pass a custom schema with agent.validator({ input: customSchema }) when the route input differs from the agent's schema.

Type Safety

If your agents have schemas, TypeScript provides full type checking:

import { createAgent } from '@agentuity/runtime';
import { s } from '@agentuity/schema';
 
// Agent with schema (src/agent/chat/agent.ts)
const chatAgent = createAgent('Chat', {
  schema: {
    input: s.object({ message: s.string() }),
    output: s.object({ response: s.string(), tokensUsed: s.number() }),
  },
  handler: async (ctx, input) => { ... },
});
 
// In route - TypeScript knows the types
import chat from '@agent/chat';
 
router.post('/chat', async (c) => {
  const result = await chat.run({ message: 'Hello' });
  // result is typed as { response: string, tokensUsed: number }
 
  return c.json({ text: result.response });
});

Next Steps

Need Help?

Join our DiscordCommunity for assistance or just to hang with other humans building agents.

Send us an email at hi@agentuity.com if you'd like to get in touch.

Please Follow us on

If you haven't already, please Signup for your free account now and start building your first agent!