Frontend API Usage Guide¶
This guide covers every endpoint that Agentomatic exposes per agent and
shows how to consume them from a frontend application. All examples use the
actual request / response models defined in router_factory.py.
Base URL convention
Every agent is mounted at /api/v1/{agent_name}/. There is no
/agents/ segment in the path. For an agent called alpha the invoke
endpoint is /api/v1/alpha/invoke.
TypeScript Interfaces¶
The interfaces below map 1-to-1 to the Pydantic models on the server.
// ── Invoke ──────────────────────────────────────────────────────
/** Maps to AgentInvokeRequest (router_factory.py) */
interface AgentInvokeRequest {
query: string; // required
user_id?: string; // default: "default-user"
context?: Record<string, any>; // default: {}
thread_id?: string | null; // default: null
prompt_version?: string; // default: "v1"
temperature?: number | null; // 0.0 – 2.0
max_tokens?: number | null; // >= 1
metadata?: Record<string, any>; // default: {}
}
/** Maps to AgentInvokeResponse (router_factory.py) */
interface AgentInvokeResponse {
response: string;
agent_type: string;
thread_id: string | null;
suggestions: string[];
citations: Record<string, any>[];
steps_taken: string[];
context: any;
metadata: Record<string, any>;
duration_ms: number;
}
// ── Chat ────────────────────────────────────────────────────────
/** Maps to AgentChatRequest (router_factory.py) */
interface AgentChatRequest {
content: string; // required – user message
user_id?: string; // default: "default-user"
thread_id?: string | null; // default: null
context?: Record<string, any>; // default: {}
metadata?: Record<string, any>; // default: {}
messages?: { role: string; content: string }[] | null; // override history
include_history?: boolean; // default: true
max_history?: number | null; // override agent default
persist?: boolean; // default: true
prompt_version?: string; // default: "v1"
}
// ── Threads ─────────────────────────────────────────────────────
interface CreateThreadRequest {
thread_id?: string | null; // auto-generated if omitted
user_id?: string; // default: "default-user"
title?: string | null;
metadata?: Record<string, any>;
}
interface UpdateThreadRequest {
title?: string | null;
metadata?: Record<string, any> | null;
}
// ── Feedback ────────────────────────────────────────────────────
interface FeedbackPayload {
thread_id: string;
rating: number; // e.g. 1–5
comment?: string;
metadata?: Record<string, any>;
}
// ── Tasks (async / batch execution) ─────────────────────────────
/** Maps to TaskProgress (tasks/models.py) */
interface TaskProgress {
percent: number; // 0–100
message: string;
current?: number | null;
total?: number | null;
stage?: string | null;
}
/** Maps to TaskRecord.public_dict() (tasks/models.py) */
interface TaskRecord {
id: string; // "task_..."
target_type: "agent" | "plugin" | "pipeline" | "endpoint" | "ingestion";
target: string; // resource name
mode: "sync" | "async" | "batch" | "stream";
status: "queued" | "running" | "succeeded" | "failed" | "cancelled";
progress: TaskProgress;
result?: any; // populated once succeeded
error?: string | null; // populated once failed
created_at: number;
started_at?: number | null;
finished_at?: number | null;
duration_ms?: number | null;
links?: { status: string; events: string; result: string; cancel: string };
}
Core Endpoints¶
1. Invoke — synchronous request / response 🎯¶
POST /api/v1/{agent_name}/invoke
Use this for the vast majority of frontend interactions.
const res = await fetch("/api/v1/alpha/invoke", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: "What is machine learning?",
user_id: "user-42",
context: { domain: "education" },
temperature: 0.7,
}),
});
const data: AgentInvokeResponse = await res.json();
console.log(data.response); // "Machine learning is …"
console.log(data.duration_ms); // 432.1
console.log(data.steps_taken); // ["retrieve", "generate"]
2. Streaming — SSE (Server-Sent Events) ⚡¶
POST /api/v1/{agent_name}/invoke/stream
Same request body as /invoke. The response is an text/event-stream.
const res = await fetch("/api/v1/alpha/invoke/stream", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: "Explain quantum computing step by step",
}),
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split("\n");
for (const line of lines) {
if (line.startsWith("data: ")) {
const payload = line.slice(6);
if (payload === "[DONE]") return;
if (payload.startsWith("[ERROR]")) {
console.error("Stream error:", payload);
return;
}
// Append token to the UI
appendToken(payload);
}
}
}
3. Chat — session-aware conversation 💬¶
POST /api/v1/{agent_name}/chat
Automatically manages conversation history, thread creation, and message persistence when a thread store is configured.
const res = await fetch("/api/v1/alpha/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
content: "Explain quantum computing",
user_id: "user-42",
thread_id: "thread-abc", // reuse an existing thread
include_history: true, // auto-load prior messages
persist: true, // save this exchange
}),
});
const data = await res.json();
console.log(data.response);
console.log(data.thread_id); // "thread-abc"
4. Async Tasks — long-running jobs with progress ⏳¶
Any resource (agent, plugin, pipeline, endpoint, ingestor) can run as a tracked background task. This is ideal for long jobs — e.g. document ingestion — where the frontend submits work, then polls or streams progress.
Submit (returns immediately with 202 and a task record):
- Agent:
POST /api/v1/{agent}/invoke/async(single) ·/invoke/batch(many) - Plugin:
POST /api/v1/{plugin}/predict/async·/predict/batch - Pipeline:
POST /api/v1/pipelines/{name}/run/async·/run/batch - Endpoint:
POST /api/v1/endpoints/{name}{path}/async·.../batch - Ingestor:
POST /api/v1/ingestion/{name}/run/async·/run/batch
// 1. Submit an async task
const submit = await fetch("/api/v1/alpha/invoke/async", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "Summarise this 400-page report" }),
});
const task: TaskRecord = await submit.json();
// task.status === "queued"; task.links.status === "/api/v1/tasks/task_ab12..."
// 2a. Poll the task board until terminal
async function pollTask(taskId: string): Promise<TaskRecord> {
while (true) {
const res = await fetch(`/api/v1/tasks/${taskId}`);
const rec: TaskRecord = await res.json();
updateProgressBar(rec.progress.percent, rec.progress.message);
if (["succeeded", "failed", "cancelled"].includes(rec.status)) return rec;
await new Promise((r) => setTimeout(r, 750));
}
}
const final = await pollTask(task.id);
console.log(final.result); // once succeeded
Or stream progress live via SSE instead of polling:
// 2b. Stream progress events
const es = new EventSource(`/api/v1/tasks/${task.id}/events`);
es.onmessage = (e) => {
const evt = JSON.parse(e.data); // { status, progress, ... }
updateProgressBar(evt.progress.percent, evt.progress.message);
if (["succeeded", "failed", "cancelled"].includes(evt.status)) es.close();
};
Cancel an in-flight task:
The task board endpoints (not agent-scoped):
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/tasks |
Submit a task for any target ({ target_type, target, input }) |
GET |
/api/v1/tasks |
List/filter tasks (status, target_type, target, limit, offset) |
GET |
/api/v1/tasks/{id} |
Task status, progress, result |
GET |
/api/v1/tasks/{id}/result |
Terminal result payload |
GET |
/api/v1/tasks/{id}/events |
SSE progress stream |
POST |
/api/v1/tasks/{id}/cancel |
Request cancellation |
DELETE |
/api/v1/tasks/{id} |
Delete a task record |
Batch jobs
POST .../batch accepts { "inputs": [...], "batch_concurrency"?, "callback_url"? }
and returns one task whose progress tracks per-item completion.
React task-polling hook¶
import { useCallback, useState } from "react";
export function useAsyncTask(agentName: string) {
const [task, setTask] = useState<TaskRecord | null>(null);
const [running, setRunning] = useState(false);
const run = useCallback(
async (request: AgentInvokeRequest) => {
setRunning(true);
const submit = await fetch(`/api/v1/${agentName}/invoke/async`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
let rec: TaskRecord = await submit.json();
setTask(rec);
while (!["succeeded", "failed", "cancelled"].includes(rec.status)) {
await new Promise((r) => setTimeout(r, 750));
rec = await fetch(`/api/v1/tasks/${rec.id}`).then((r) => r.json());
setTask(rec);
}
setRunning(false);
return rec;
},
[agentName],
);
return { run, task, running };
}
Agent Metadata Endpoints¶
Health¶
GET /api/v1/{agent_name}/health
const res = await fetch("/api/v1/alpha/health");
const health = await res.json();
// { "status": "ok", "agent": "alpha" }
Configuration¶
GET /api/v1/{agent_name}/config
Returns the agent's manifest and runtime configuration.
Prompt Versions¶
GET /api/v1/{agent_name}/prompts
Lists all available prompt versions for the agent.
Agent Card (A2A)¶
GET /api/v1/{agent_name}/card
Returns the agent's A2A-compatible card with capability declarations and endpoint URLs.
const res = await fetch("/api/v1/alpha/card");
const card = await res.json();
// {
// "name": "alpha",
// "description": "...",
// "version": "1.0.0",
// "capabilities": { "streaming": true, "chat": true, ... },
// "endpoints": {
// "invoke": "/api/v1/alpha/invoke",
// "chat": "/api/v1/alpha/chat",
// "stream": "/api/v1/alpha/invoke/stream",
// "health": "/api/v1/alpha/health"
// }
// }
Thread Management¶
All thread endpoints require a thread store to be configured on the platform.
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/{name}/threads |
Create a new thread |
GET |
/api/v1/{name}/threads |
List threads |
GET |
/api/v1/{name}/threads/{tid} |
Get thread details |
PATCH |
/api/v1/{name}/threads/{tid} |
Update thread title/metadata |
DELETE |
/api/v1/{name}/threads/{tid} |
Delete a thread |
GET |
/api/v1/{name}/threads/{tid}/messages |
Get thread messages |
DELETE |
/api/v1/{name}/threads/{tid}/messages |
Clear thread messages |
GET |
/api/v1/{name}/threads/{tid}/summary |
Get conversation summary |
POST |
/api/v1/{name}/threads/{tid}/fork |
Fork a thread |
GET |
/api/v1/{name}/threads/{tid}/lineage |
Get thread lineage |
Example: create & use a thread¶
// 1. Create a thread
const createRes = await fetch("/api/v1/alpha/threads", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
user_id: "user-42",
title: "ML Questions",
}),
});
const thread = await createRes.json();
const threadId = thread.thread_id;
// 2. Chat within that thread (history auto-loads)
const chatRes = await fetch("/api/v1/alpha/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
content: "What is gradient descent?",
thread_id: threadId,
user_id: "user-42",
}),
});
// 3. Retrieve messages later
const msgsRes = await fetch(
`/api/v1/alpha/threads/${threadId}/messages`
);
const messages = await msgsRes.json();
Human-in-the-Loop¶
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/{name}/threads/{tid}/pending |
Check for pending approvals |
POST |
/api/v1/{name}/threads/{tid}/approve |
Approve a pending action |
POST |
/api/v1/{name}/threads/{tid}/reject |
Reject a pending action |
Feedback¶
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/{name}/feedback |
Submit feedback |
GET |
/api/v1/{name}/feedback |
List feedback for the agent |
GET |
/api/v1/{name}/feedback/export |
Export feedback as JSONL |
A2A (Agent-to-Agent) Protocol¶
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/{name}/a2a/tasks |
Submit an A2A task |
GET |
/api/v1/{name}/a2a/tasks/{task_id} |
Get A2A task status |
Optimization¶
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/{name}/optimize/invoke |
Invoke with full optimization context |
Full Endpoint Reference¶
Every agent exposes 26 endpoints. All paths below are relative to the
agent prefix /api/v1/{agent_name}.
| # | Method | Path | Request Body | Description |
|---|---|---|---|---|
| 1 | POST |
/invoke |
AgentInvokeRequest |
Synchronous invocation |
| 2 | POST |
/invoke/stream |
AgentInvokeRequest |
SSE streaming invocation |
| 3 | POST |
/chat |
AgentChatRequest |
Session-aware chat |
| 4 | GET |
/health |
— | Agent health check |
| 5 | GET |
/config |
— | Agent configuration |
| 6 | GET |
/prompts |
— | Available prompt versions |
| 7 | GET |
/card |
— | A2A agent card |
| 8 | POST |
/a2a/tasks |
A2ATaskRequest |
Submit A2A task |
| 9 | GET |
/a2a/tasks/{task_id} |
— | Get A2A task status |
| 10 | POST |
/threads |
CreateThreadRequest |
Create thread |
| 11 | GET |
/threads |
— | List threads |
| 12 | GET |
/threads/{tid} |
— | Get thread |
| 13 | PATCH |
/threads/{tid} |
UpdateThreadRequest |
Update thread |
| 14 | DELETE |
/threads/{tid} |
— | Delete thread |
| 15 | GET |
/threads/{tid}/messages |
— | Get messages |
| 16 | DELETE |
/threads/{tid}/messages |
— | Clear messages |
| 17 | GET |
/threads/{tid}/summary |
— | Conversation summary |
| 18 | POST |
/optimize/invoke |
OptimizeInvokeRequest |
Optimization invocation |
| 19 | POST |
/feedback |
Feedback payload | Submit feedback |
| 20 | GET |
/feedback |
— | List feedback |
| 21 | GET |
/feedback/export |
— | Export feedback (JSONL) |
| 22 | GET |
/threads/{tid}/pending |
— | Pending approvals |
| 23 | POST |
/threads/{tid}/approve |
— | Approve pending action |
| 24 | POST |
/threads/{tid}/reject |
— | Reject pending action |
| 25 | POST |
/threads/{tid}/fork |
— | Fork thread |
| 26 | GET |
/threads/{tid}/lineage |
— | Thread lineage |
Plus execution-mode routes
When the task engine is enabled (default), each agent also exposes
POST /invoke/async and POST /invoke/batch that submit work to the
task board. Plugins, pipelines,
endpoints, and ingestors get the equivalent /async and /batch variants
of their primary route.
Platform Surfaces (beyond a single agent)¶
Besides the per-agent endpoints above, the platform exposes cross-cutting surfaces a frontend can consume. These power the Agentomatic Studio views (Control, Endpoints, Connections, Pipelines, Plugins).
Invocation logs (multi-resource)¶
When logs_history is enabled, use the cross-resource logs API (filter by
resource + name). Agent-scoped /{agent}/logs remains for BC. There are
no per-plugin convenience /logs routes; async task runs are not persisted
here — see Platform Features.
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/logs?resource=plugin&name=… |
List logs (resource: agent|plugin|pipeline|ingestion|endpoint) |
GET |
/api/v1/logs/{id} |
Fetch one log |
POST |
/api/v1/logs/analyze |
LLM/heuristic analysis (resource + name in body) |
GET |
/api/v1/logs/analysis?resource=…&name=… |
Latest analysis |
Control Plane¶
Mounted at {api_prefix}/control when enable_control_plane=True. Mutating
calls require the X-Control-Token header if a control_token is configured.
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/control |
Platform overview (counts, uptime, maintenance flag) |
GET |
/api/v1/control/agents |
Agents with health, effective auth policy, connections |
GET |
/api/v1/control/agents/{name} |
Single-agent operational detail |
GET |
/api/v1/control/endpoints |
Registered custom endpoints |
GET |
/api/v1/control/connections |
Connection health by scope |
GET |
/api/v1/control/health |
Aggregate health (agents + connections) |
GET |
/api/v1/control/metrics/summary |
Coarse counters for dashboards |
GET |
/api/v1/control/config |
Sanitised feature/config snapshot |
POST |
/api/v1/control/agents/{name}/disable |
Drain an agent (503 for its routes) |
POST |
/api/v1/control/agents/{name}/enable |
Re-enable an agent |
POST |
/api/v1/control/maintenance |
Toggle maintenance mode ({ "enabled": bool }) |
// Overview + agent list
const info = await fetch("/api/v1/control").then((r) => r.json());
const agents = await fetch("/api/v1/control/agents").then((r) => r.json());
// Drain an agent (requires control token when configured)
await fetch("/api/v1/control/agents/fraud_agent/disable", {
method: "POST",
headers: { "X-Control-Token": CONTROL_TOKEN },
});
/** Maps to ControlAgentInfo (control/models.py) */
interface ControlAgentInfo {
name: string;
slug: string;
description: string;
version: string;
framework: string;
enabled: boolean;
requires_auth: boolean;
allowed_roles: string[];
allowed_scopes: string[];
connections: string[];
health: Record<string, any>;
}
/** Maps to ControlEndpointInfo */
interface ControlEndpointInfo {
name: string;
description: string;
version: string;
path: string;
methods: string[];
aggregation: string;
upstreams: string[];
ready: boolean;
}
/** Maps to ControlConnectionInfo */
interface ControlConnectionInfo {
scope: string;
connections: Record<string, any>;
}
Custom Endpoints¶
Each custom endpoint is mounted under {api_prefix}/endpoints/{name}. The main
handler lives at the endpoint's configurable path (default /call), with
/health and /info alongside it. List all endpoints via GET
/api/v1/endpoints or the control plane. Use these to enrich a UI with
model-ensemble results.
// Default handler path is `/call` (override via the endpoint's `path` attr)
const result = await fetch("/api/v1/endpoints/ensemble/call", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ payload: { text: "classify me" } }),
}).then((r) => r.json());
Pipelines & Plugins¶
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/pipelines |
List pipelines |
POST |
/api/v1/pipelines/{name}/run |
Run a pipeline ({ input, metadata? }) |
GET |
/api/v1/pipelines/{name}/validate |
Validate a pipeline |
GET |
/api/v1/pipelines/{name}/visualize |
Mermaid diagram of the flow |
GET |
/api/v1/plugins |
List ML plugins |
GET |
/api/v1/plugins/{name}/model_card |
Plugin model card |
POST |
/api/v1/plugins/{name}/predict |
Run a plugin prediction |
Ingestion / RAG¶
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/ingestion |
List registered ingestors |
POST |
/api/v1/ingestion/{name}/run |
Run an ingestion job (sync) |
POST |
/api/v1/ingestion/{name}/run/async |
Run as a tracked task |
GET |
/api/v1/ingestion/{name}/info |
Ingestor metadata / readiness |
Status Dashboard¶
A single roll-up of the whole platform's health — ideal for an ops/admin view.
| Method | Path | Description |
|---|---|---|
GET |
/status |
HTML dashboard (all resources + task engine) |
GET |
/api/v1/status |
JSON: overall + per-resource health |
const status = await fetch("/api/v1/status").then((r) => r.json());
// {
// status: "healthy", // overall rollup
// platform: { name, version, uptime_seconds, maintenance_mode },
// summary: { agents: { total, healthy }, plugins: {...}, ... },
// resources: {
// agents: { total, healthy, degraded, items: { ...per-agent health } },
// plugins: { ... }, endpoints: { ... },
// ingestors: { ... }, pipelines: { ... },
// },
// tasks: { enabled, running, queued, ... }, // task-engine stats
// storage: { status, backend, ... },
// }
Auth for platform surfaces
When JWT auth is enabled, send the same Authorization: Bearer <token>
header used for agent calls. The control plane's mutating routes
additionally require X-Control-Token when a control token is configured.
Framework Integration Examples¶
React Hook¶
import { useState, useCallback } from "react";
interface UseAgentOptions {
agentName: string;
baseUrl?: string;
}
export function useAgent({ agentName, baseUrl = "" }: UseAgentOptions) {
const [loading, setLoading] = useState(false);
const [response, setResponse] = useState<AgentInvokeResponse | null>(null);
const [error, setError] = useState<string | null>(null);
const invoke = useCallback(
async (request: AgentInvokeRequest) => {
setLoading(true);
setError(null);
try {
const res = await fetch(
`${baseUrl}/api/v1/${agentName}/invoke`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
},
);
if (!res.ok) {
const err = await res.json();
throw new Error(
typeof err.detail === "string"
? err.detail
: err.detail?.error ?? `Request failed (${res.status})`,
);
}
const data: AgentInvokeResponse = await res.json();
setResponse(data);
return data;
} catch (err) {
const msg = err instanceof Error ? err.message : "Unknown error";
setError(msg);
throw err;
} finally {
setLoading(false);
}
},
[agentName, baseUrl],
);
return { invoke, loading, response, error };
}
// ── Usage ─────────────────────────────────────────────────────
function ChatComponent() {
const { invoke, loading, response, error } = useAgent({
agentName: "alpha",
});
const handleSubmit = async (query: string) => {
await invoke({ query, user_id: "user-42" });
};
return (
<div>
{loading && <div>Processing…</div>}
{error && <div className="error">Error: {error}</div>}
{response && (
<div>
<p>{response.response}</p>
<small>{response.duration_ms.toFixed(0)} ms</small>
</div>
)}
</div>
);
}
React Streaming Hook¶
import { useState, useCallback, useRef } from "react";
export function useAgentStream(agentName: string) {
const [streaming, setStreaming] = useState(false);
const [tokens, setTokens] = useState("");
const abortRef = useRef<AbortController | null>(null);
const stream = useCallback(
async (request: AgentInvokeRequest) => {
abortRef.current?.abort();
const controller = new AbortController();
abortRef.current = controller;
setStreaming(true);
setTokens("");
const res = await fetch(
`/api/v1/${agentName}/invoke/stream`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
signal: controller.signal,
},
);
const reader = res.body!.getReader();
const decoder = new TextDecoder();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
for (const line of chunk.split("\n")) {
if (!line.startsWith("data: ")) continue;
const data = line.slice(6);
if (data === "[DONE]") return;
if (data.startsWith("[ERROR]")) throw new Error(data);
setTokens((prev) => prev + data);
}
}
} finally {
setStreaming(false);
}
},
[agentName],
);
const cancel = useCallback(() => abortRef.current?.abort(), []);
return { stream, streaming, tokens, cancel };
}
Vue.js Composable¶
import { ref, readonly } from "vue";
export function useAgent(agentName: string, baseUrl = "") {
const loading = ref(false);
const response = ref<AgentInvokeResponse | null>(null);
const error = ref<string | null>(null);
async function invoke(request: AgentInvokeRequest) {
loading.value = true;
error.value = null;
try {
const res = await fetch(
`${baseUrl}/api/v1/${agentName}/invoke`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
},
);
if (!res.ok) {
const err = await res.json();
throw new Error(
typeof err.detail === "string"
? err.detail
: err.detail?.error ?? `Request failed (${res.status})`,
);
}
const data: AgentInvokeResponse = await res.json();
response.value = data;
return data;
} catch (err: any) {
error.value = err.message ?? "Unknown error";
throw err;
} finally {
loading.value = false;
}
}
async function chat(request: AgentChatRequest) {
loading.value = true;
error.value = null;
try {
const res = await fetch(
`${baseUrl}/api/v1/${agentName}/chat`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
},
);
if (!res.ok) {
const err = await res.json();
throw new Error(err.detail?.error ?? `Request failed`);
}
const data = await res.json();
response.value = data;
return data;
} catch (err: any) {
error.value = err.message ?? "Unknown error";
throw err;
} finally {
loading.value = false;
}
}
return {
invoke,
chat,
loading: readonly(loading),
response: readonly(response),
error: readonly(error),
};
}
Error Handling¶
Agentomatic returns standard FastAPI error responses. All errors use the
detail field.
interface APIError {
detail:
| string
| {
error: string;
agent: string;
validation_errors?: any[];
expected_schema?: string;
};
}
async function safeInvoke(
agentName: string,
request: AgentInvokeRequest,
): Promise<AgentInvokeResponse> {
const res = await fetch(`/api/v1/${agentName}/invoke`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
});
if (!res.ok) {
const err: APIError = await res.json();
switch (res.status) {
case 404:
throw new Error(`Agent "${agentName}" not found`);
case 422:
if (typeof err.detail === "object") {
console.error("Validation errors:", err.detail.validation_errors);
console.error("Expected schema:", err.detail.expected_schema);
}
throw new Error("Invalid request body");
case 500:
throw new Error("Internal server error");
default:
throw new Error(
typeof err.detail === "string"
? err.detail
: JSON.stringify(err.detail),
);
}
}
return res.json();
}
Quick Reference¶
Rules of thumb
- Use
/invokefor standard request → response interactions (95% of use cases). - Use
/invoke/streamwhen you need token-by-token streaming in a chat UI. - Use
/chatwhen you want automatic conversation history, thread management, and message persistence. - All three endpoints work with every agent — the platform generates them automatically.
Common mistakes
- ❌
/api/v1/agents/alpha/invoke— wrong, there is no/agents/segment. - ✅
/api/v1/alpha/invoke— correct. - ❌
{ payload: { query: "..." } }— wrong, nopayloadwrapping. - ✅
{ query: "..." }— correct, fields go at the top level. - ❌
{ input: "..." }for chat — wrong field name. - ✅
{ content: "..." }for chat — correct field name. - ❌
{ streaming: true }— wrong, there is nostreamingfield. - ✅ Use the
/invoke/streamendpoint instead for streaming.