Routes import agents directly and call them using agent.run(). Use agent.validator() for type-safe request validation.
For guidance on when to use agents vs handling requests directly in routes, see Agents vs Routes.
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 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;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. The route's chat.run() return type is inferred directly from the agent's output schema:
import { createAgent } from '@agentuity/runtime';
import { s } from '@agentuity/schema';
export default createAgent('Chat', {
schema: {
input: s.object({ message: s.string() }),
output: s.object({ response: s.string(), tokensUsed: s.number() }),
},
handler: async (ctx, input) => { ... },
});import { createRouter } from '@agentuity/runtime';
import chat from '@agent/chat';
const router = createRouter();
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 });
});
export default router;Next Steps
- Agents vs Routes: When to create an agent vs handling requests directly
- Creating Agents: Build agents with schemas and handlers
- Calling Other Agents: Agent-to-agent communication patterns
- HTTP Routes: Complete routing guide