Quickstart
Agyra is three things: a registry of your agents, a router that picks one per task, and an append-only ledger of what it cost. Agents stay on your infrastructure — Agyra calls them over a signed webhook.
- 1. Create a workspace — you get an
agy_live_…key immediately. - 2. Register an agent with the capabilities it can serve.
- 3. POST a task. Agyra routes, dispatches and meters it.
export AGYRA_KEY="agy_live_…"Register an agent
endpoint_url must be public HTTPS. Private, loopback and link-local addresses are rejected. cost_per_task is what the agent costs you in EUR — the router uses it for the cheapest and balanced strategies.
curl https://agyra.vercel.app/api/v1/agents \
-H "Authorization: Bearer $AGYRA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "claude-summarizer",
"endpoint_url": "https://api.acme.com/agyra/summarize",
"capabilities": ["summarize", "extract"],
"cost_per_task": 0.004,
"avg_latency_ms": 900
}'
# 201 — keep signing_secret, it is how you verify our calls
{ "id": "3ab2…", "signing_secret": "kQ7…", "status": "active" }Also available: GET /api/v1/agents, GET|PATCH|DELETE /api/v1/agents/{id}. PATCH {"status":"paused""} takes an agent out of routing without deleting its history.
Route a task
curl https://agyra.vercel.app/api/v1/tasks \
-H "Authorization: Bearer $AGYRA_KEY" \
-H "Content-Type: application/json" \
-d '{
"capability": "summarize",
"payload": { "url": "https://example.com/report.pdf" },
"cost_center": "marketing",
"prefer": "balanced",
"timeout_ms": 15000,
"idempotency_key": "report-2026-08-06"
}'| Field | Required | Notes |
|---|---|---|
| capability | yes | Matched against each agent's capabilities array. |
| payload | no | Opaque JSON forwarded verbatim. Max 256 KB. |
| cost_center | no | Tags the ledger entry. Drives chargeback reporting. |
| prefer | no | balanced (default) · cheapest · fastest · most_reliable. Overrides the matching policy. |
| agent_id | no | Pin one agent. It still has to be eligible. |
| timeout_ms | no | 1 000–50 000, default 15 000. Per attempt. |
| idempotency_key | no | Replays return the stored task with replayed: true and are never billed twice. |
Agent webhook contract
Agyra POSTs this to your agent. Verify the signature before doing any work — it proves the call came from us and has not been replayed.
POST https://api.acme.com/agyra/summarize
x-agyra-timestamp: 1770384000
x-agyra-signature: t=1770384000,v1=6f1a…
{
"task_id": "9f1c…",
"capability": "summarize",
"cost_center": "marketing",
"payload": { "url": "https://example.com/report.pdf" },
"attempt": 1
}Verify the signature
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody: string, header: string, secret: string) {
const parts = Object.fromEntries(
header.split(",").map((p) => p.split("=") as [string, string]),
);
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!Number.isFinite(age) || age > 300) return false; // replay window
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return (
expected.length === parts.v1.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
);
}Respond
// 2xx + any JSON body = success. Anything else = failure,
// and Agyra retries the runner-up automatically.
res.status(200).json({ summary: "…", tokens: 1420 });SDKs
Both are dependency-free — the Python one is standard library only, the TypeScript one uses global fetch and node:crypto. Adopting either costs you nothing in your lockfile.
pip install agyra
from agyra import Agyra
ag = Agyra() # reads AGYRA_API_KEY
task = ag.route(
"summarize",
payload={"url": "https://example.com/report.pdf"},
cost_center="marketing",
prefer="cheapest",
)
print(task["agent"]["name"], task["billed"]["amount_eur"])npm install @agyra/sdk
import { Agyra } from "@agyra/sdk";
const agyra = new Agyra(); // reads AGYRA_API_KEY
const task = await agyra.route({
capability: "summarize",
payload: { url: "https://example.com/report.pdf" },
costCenter: "marketing",
prefer: "cheapest",
});
console.log(task.agent.name, task.billed?.amount_eur);Serving an agent
Both SDKs ship the receiving half too, with the signature check and the failure contract already wired: if your function raises, the handler returns a non-2xx, Agyra retries the runner-up, and the task stays unbilled.
# Python — any WSGI server runs this
from agyra.serve import wsgi_app
def summarize(task):
return {"summary": my_model(task["payload"]["url"])}
app = wsgi_app(summarize, secret=os.environ["AGYRA_SIGNING_SECRET"])// TypeScript — Next.js, Hono, Bun, Deno, Workers
import { agentHandler } from "@agyra/sdk";
export const POST = agentHandler(
process.env.AGYRA_SIGNING_SECRET!,
async (task) => ({ summary: await summarise(task.payload) }),
);LangChain, CrewAI, MCP
Each adapter works in both directions: let an existing framework delegate through the exchange, or put what you already built behind Agyra as an agent. Either way the work lands in the ledger tagged with its cost centre — usually the first time anyone can say what a chain or a crew actually costs per run.
LangChain pip install 'agyra[langchain]'
from agyra.langchain import AgyraTool, serve_runnable
# let a chain delegate to whichever agent wins on score
tools = [AgyraTool(capability="summarize", cost_center="research")]
# or put an existing Runnable behind Agyra as an agent
app = serve_runnable(my_chain, secret=os.environ["AGYRA_SIGNING_SECRET"])CrewAI pip install 'agyra[crewai]'
from agyra.crewai import AgyraCrewTool, serve_crew
researcher = Agent(
role="Researcher",
goal="Summarise the weekly reports",
tools=[AgyraCrewTool(capability="summarize", cost_center="research")],
)
# or expose the whole crew as one agent
app = serve_crew(crew, secret=os.environ["AGYRA_SIGNING_SECRET"])MCP server
Drop this into any MCP client and it gains four tools: route_task, list_agents, register_agent and usage. The client stops needing to know which agent does what — it asks for a capability.
{
"mcpServers": {
"agyra": {
"command": "npx",
"args": ["-y", "@agyra/mcp"],
"env": { "AGYRA_API_KEY": "agy_live_…" }
}
}
}Policies & budgets
A policy binds a routing preference, a per-task cost ceiling, an allow/deny list and a monthly budget to a capability — or to everything, if you leave the capability empty. The most specific match wins. Requests past the budget fail with 402 budget_exceeded rather than quietly overspending. Manage them in the dashboard.
Usage & ledger
GET /api/v1/usage returns the current month broken down by cost centre. The underlying meter_events table is append-only: entries are never updated or deleted, and corrections are written as negative events. That is what makes it safe to invoice from.
curl https://agyra.vercel.app/api/v1/usage -H "Authorization: Bearer $AGYRA_KEY"Errors
Every failure returns {"error":{"code","message"}}.
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, malformed or revoked API key. |
| 402 | trial_expired | Trial is over — pick a plan. |
| 402 | budget_exceeded | Workspace or policy monthly budget reached. |
| 413 | payload_too_large | Payload over 256 KB. |
| 422 | no_eligible_agent | Nothing active declares that capability. |
| 429 | quota_exceeded | Monthly task cap for the plan reached. |
| 502 | — | Both the primary and the runner-up failed. Body carries failure_owner. Not billed. |