Use Agentuity services from external apps, scripts, or backends. Standalone packages are the shortest path, while @agentuity/server gives you lower-level control over adapters and service URLs.
Standalone Packages
The fastest way to access Agentuity services from external apps. Each package reads AGENTUITY_SDK_KEY, AGENTUITY_REGION, and service URL overrides from the environment.
Set AGENTUITY_SDK_KEY in your .env file. Set AGENTUITY_REGION when you need a region other than the default usc, or when matching an existing Agentuity project. See Standalone Packages for details.
import { KeyValueClient } from '@agentuity/keyvalue';
import { VectorClient } from '@agentuity/vector';
import { SandboxClient } from '@agentuity/sandbox';
export const kv = new KeyValueClient();
export const vector = new VectorClient();
export const sandbox = new SandboxClient();import { kv } from '../../lib/agentuity';
export async function GET(request: Request) {
const userId = new URL(request.url).searchParams.get('userId');
if (!userId) {
return Response.json({ error: 'Missing userId' }, { status: 400 });
}
const result = await kv.get<{ messages: string[] }>('sessions', userId);
if (result.exists) {
return Response.json(result.data);
}
return Response.json({ messages: [] });
}See Standalone Packages for the full list of available packages and their APIs.
Manual Configuration
For custom adapter configuration, direct service URL overrides, or when you need lower-level control, use @agentuity/server directly.
Add these environment variables to your .env:
AGENTUITY_SDK_KEY=your_sdk_key # From Agentuity console or project .env
AGENTUITY_REGION=use # From agentuity.json (region field)Your region is in your Agentuity project's agentuity.json file. If you forget to set AGENTUITY_REGION, the SDK throws a helpful error telling you what's missing.
import {
KeyValueStorageService,
VectorStorageService,
StreamStorageService,
buildClientHeaders,
createLogger,
createServerFetchAdapter,
getServiceUrls,
} from '@agentuity/server';
const logger = createLogger('info');
const sdkKey = process.env.AGENTUITY_SDK_KEY;
const region = process.env.AGENTUITY_REGION;
if (!sdkKey) {
throw new Error('AGENTUITY_SDK_KEY environment variable is required');
}
if (!region) {
throw new Error('AGENTUITY_REGION environment variable is required');
}
const adapter = createServerFetchAdapter({
headers: buildClientHeaders({ apiKey: sdkKey }),
}, logger);
const urls = getServiceUrls(region);
export const kv = new KeyValueStorageService(urls.keyvalue, adapter);
export const vector = new VectorStorageService(urls.vector, adapter);
export const stream = new StreamStorageService(urls.stream, adapter);Use the client in your Next.js backend:
import type { NextRequest } from 'next/server';
import { kv } from '@/lib/agentuity-storage';
interface ChatSession {
messages: Array<{ role: string; content: string }>;
createdAt: string;
}
export async function GET(request: NextRequest) {
const sessionId = request.nextUrl.searchParams.get('id');
if (!sessionId) {
return Response.json({ error: 'Missing session ID' }, { status: 400 });
}
const result = await kv.get<ChatSession>('sessions', sessionId);
if (!result.exists) {
return Response.json({ error: 'Not found' }, { status: 404 });
}
return Response.json(result.data);
}
export async function POST(request: NextRequest) {
const { sessionId, messages } = await request.json();
await kv.set('sessions', sessionId, {
messages,
createdAt: new Date().toISOString(),
}, { ttl: 86400 });
return Response.json({ success: true });
}Vector Storage
import type { NextRequest } from 'next/server';
import { vector } from '@/lib/agentuity-storage';
export async function POST(request: NextRequest) {
const { query, limit = 10 } = await request.json();
const results = await vector.search('documents', {
query,
limit,
});
return Response.json(results);
}Stream Storage
Use streams for large data exports or file transfers:
import type { NextRequest } from 'next/server';
import { stream } from '@/lib/agentuity-storage';
export async function GET(request: NextRequest) {
const id = request.nextUrl.searchParams.get('id');
if (!id) {
return Response.json({ error: 'Missing stream ID' }, { status: 400 });
}
const data = await stream.download(id);
return new Response(data, {
headers: { 'Content-Type': 'application/octet-stream' },
});
}Queue Management
Manage queues programmatically from external apps or scripts using APIClient:
import { APIClient, createLogger, getServiceUrls } from '@agentuity/server';
const region = process.env.AGENTUITY_REGION;
const sdkKey = process.env.AGENTUITY_SDK_KEY;
if (!region) {
throw new Error('AGENTUITY_REGION environment variable is required');
}
if (!sdkKey) {
throw new Error('AGENTUITY_SDK_KEY environment variable is required');
}
export const logger = createLogger('info');
const urls = getServiceUrls(region);
export const client = new APIClient(
urls.catalyst,
logger,
sdkKey
);Creating and Managing Queues
import {
createQueue,
listQueues,
deleteQueue,
pauseQueue,
resumeQueue,
} from '@agentuity/server';
import { client } from '@/lib/agentuity-queues';
// Create a worker queue
const queue = await createQueue(client, {
name: 'order-processing',
queue_type: 'worker',
settings: {
default_max_retries: 5,
default_visibility_timeout_seconds: 60,
},
});
// List all queues
const { queues } = await listQueues(client);
// Pause and resume
await pauseQueue(client, 'order-processing');
await resumeQueue(client, 'order-processing');
// Delete a queue
await deleteQueue(client, 'old-queue');Dead Letter Queue Operations
import {
listDeadLetterMessages,
replayDeadLetterMessage,
purgeDeadLetter,
} from '@agentuity/server';
import { client, logger } from '@/lib/agentuity-queues';
// List failed messages
const { messages } = await listDeadLetterMessages(client, 'order-processing');
for (const msg of messages) {
logger.warn('Failed message', { id: msg.id, reason: msg.failure_reason });
// Replay back to the queue
await replayDeadLetterMessage(client, 'order-processing', msg.id);
}
// Purge all DLQ messages
await purgeDeadLetter(client, 'order-processing');Webhook Destinations
import { createDestination } from '@agentuity/server';
import { client } from '@/lib/agentuity-queues';
const webhookApiKey = process.env.WEBHOOK_API_KEY;
if (!webhookApiKey) {
throw new Error('WEBHOOK_API_KEY environment variable is required');
}
await createDestination(client, 'order-processing', {
name: 'orders-webhook',
destination_type: 'http',
enabled: true,
config: {
url: 'https://api.example.com/webhook/orders',
method: 'POST',
headers: { 'X-API-Key': webhookApiKey },
timeout_ms: 30000,
retry_policy: {
max_attempts: 5,
initial_backoff_ms: 1000,
max_backoff_ms: 60000,
backoff_multiplier: 2.0,
},
},
});HTTP Ingestion Sources
import { createSource } from '@agentuity/server';
import { client, logger } from '@/lib/agentuity-queues';
const source = await createSource(client, 'webhook-queue', {
name: 'stripe-webhooks',
description: 'Receives Stripe payment events',
auth_type: 'header',
auth_value: `Bearer ${process.env.STRIPE_WEBHOOK_SECRET}`,
});
// External services POST to this URL
logger.info('Source created', { url: source.url });Pull-Based Consumption
For workers that pull and acknowledge messages:
import { receiveMessage, ackMessage, nackMessage } from '@agentuity/server';
import { client } from '@/lib/agentuity-queues';
// Receive a message (blocks until available or timeout)
const message = await receiveMessage(client, 'order-processing');
if (message) {
try {
await processOrder(message.payload);
await ackMessage(client, 'order-processing', message.id);
} catch (error) {
// Message returns to queue for retry
await nackMessage(client, 'order-processing', message.id);
}
}For one-off queue management, use the CLI instead: agentuity cloud queue create worker --name my-queue, agentuity cloud queue dlq list my-queue, etc. See Queues for CLI commands.
Alternative: HTTP Routes
If you want to centralize storage logic in your Agentuity project (for middleware, sharing across multiple apps, or avoiding SDK key distribution), use HTTP routes instead.
import { Hono } from 'hono';
import type { Env } from '@agentuity/runtime';
import { s } from '@agentuity/schema';
const router = new Hono<Env>();
const MessageSchema = s.object({
role: s.string(),
content: s.string(),
});
const SessionPatchSchema = s.object({
messages: s.optional(s.array(MessageSchema)),
});
type Message = s.infer<typeof MessageSchema>;
type SessionPatch = s.infer<typeof SessionPatchSchema>;
interface ChatSession {
messages: Message[];
createdAt: string;
updatedAt: string;
}
// Get a session by ID
router.get('/:id', async (c) => {
const sessionId = c.req.param('id');
const result = await c.var.kv.get<ChatSession>('sessions', sessionId);
if (!result.exists) {
return c.json({ error: 'Session not found' }, 404);
}
return c.json(result.data);
});
// Create or update a session
router.post('/:id', async (c) => {
const sessionId = c.req.param('id');
const payload = await c.req.json();
const parsed = SessionPatchSchema.safeParse(payload);
if (!parsed.success) {
return c.json({ error: 'Invalid session payload' }, 400);
}
const body: SessionPatch = parsed.data;
const existing = await c.var.kv.get<ChatSession>('sessions', sessionId);
const session: ChatSession = existing.exists
? { ...existing.data, ...body, updatedAt: new Date().toISOString() }
: { messages: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), ...body };
await c.var.kv.set('sessions', sessionId, session, { ttl: 86400 * 7 });
c.var.logger.info('Session updated', { sessionId });
return c.json(session);
});
// Delete a session
router.delete('/:id', async (c) => {
const sessionId = c.req.param('id');
await c.var.kv.delete('sessions', sessionId);
c.var.logger.info('Session deleted', { sessionId });
return c.json({ success: true });
});
export default router;Securing Storage Routes
Add authentication middleware to protect storage endpoints:
import { Hono } from 'hono';
import type { Env } from '@agentuity/runtime';
import { createMiddleware } from 'hono/factory';
const router = new Hono<Env>();
const requireAuth = createMiddleware(async (c, next) => {
const apiKey = c.req.header('x-api-key');
if (!apiKey || apiKey !== process.env.STORAGE_API_KEY) {
c.var.logger.warn('Unauthorized storage access attempt');
return c.json({ error: 'Unauthorized' }, 401);
}
await next();
});
router.use('/*', requireAuth);
// Routes now require x-api-key header
router.get('/:id', async (c) => {
// ... same as above
});
export default router;Routes also have access to c.var.vector and c.var.stream for Vector and Stream storage.
Logging with createLogger
Create loggers for scripts or external services:
import { createLogger } from '@agentuity/server';
// Create a logger with info level, no timestamps, dark color scheme
const logger = createLogger('info', false, 'dark');
logger.child({ userId: '123' }).info('Processing request');
logger.child({ error: 'Connection refused' }).error('Failed to connect');
// Child logger with persistent context
const requestLogger = logger.child({ requestId: 'req_abc' });
requestLogger.info('Starting'); // Includes requestId in all logsParameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
level | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'info' | Minimum log level |
showTimestamp | boolean | false | Include ISO timestamps |
colorScheme | 'light' | 'dark' | 'dark' | Terminal color scheme |
context | Record<string, unknown> | {} | Default context for all logs |
Safe JSON Serialization
safeStringify handles circular references and BigInt values that break JSON.stringify:
import { safeStringify } from '@agentuity/core';
const obj: Record<string, unknown> = { name: 'Alice' };
obj.self = obj;
safeStringify(obj); // '{"name":"Alice","self":"[Circular]"}'
safeStringify({ id: 9007199254740991n }); // '{"id":"9007199254740991"}'
const data = { user: obj, balance: 9007199254740991n };
safeStringify(data, 2);Error Handling Utilities
RichError
Enhanced errors with context and pretty printing:
import { RichError } from '@agentuity/core';
const originalError = new Error('Upstream request timed out');
const error = new RichError({
message: 'Request failed',
statusCode: 500,
endpoint: '/api/users',
cause: originalError,
});
error.plainArgs; // { statusCode: 500, endpoint: '/api/users' }
error.cause; // originalError (the cause chain)
error.prettyPrint(); // Formatted multi-line output with stack traces
error.toJSON(); // Serializable object for logging
error.toString(); // Same as prettyPrint()StructuredError
Type-safe, discriminated errors with a _tag for pattern matching:
import { StructuredError, isStructuredError } from '@agentuity/core';
import { createLogger } from '@agentuity/server';
const logger = createLogger('error');
// Create error types
const NotFoundError = StructuredError('NotFound');
const ValidationError = StructuredError('ValidationError')<{
field: string;
code: string;
}>();
// With a default message (message is preset, cannot be overridden)
const UpgradeRequired = StructuredError('UpgradeRequired', 'Upgrade required to access this feature');
try {
throw new ValidationError({
field: 'email',
code: 'INVALID_FORMAT',
message: 'Email format is invalid',
});
} catch (err) {
if (isStructuredError(err) && err._tag === 'ValidationError') {
logger.error('Validation failed', { field: err.field, code: err.code });
}
}Schema Conversion
Convert Agentuity schemas to JSON Schema for tooling or OpenAPI generation:
import { s } from '@agentuity/schema';
import { toJSONSchema } from '@agentuity/server';
const schema = s.object({ name: s.string(), age: s.number() });
const jsonSchema = toJSONSchema(schema);
// { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' } }, ... }Key Points
- Standalone packages are the simplest way to access services from external backends
- Manual configuration with
@agentuity/serversupports custom adapters and service URL overrides - Required env vars:
AGENTUITY_SDK_KEYandAGENTUITY_REGION(fromagentuity.json) - HTTP routes are an alternative for centralized logic, middleware, or sharing across apps
- Works with any framework: Next.js, Express, Remix, etc.
See Also
- Standalone Packages: Full list of service packages and their APIs
- Queues: Queue concepts and CLI commands
- HTTP Routes: Route creation with Hono
- Route Middleware: Authentication patterns
- RPC Client: Typed client generation