Agnose API and MCP
Use your agents from your code or from another AI agent: list your instances, message them, read their replies and your usage.
Authentication
Create a personal key in your account and send it on every call as Authorization: Bearer <key>. Each key has scopes and can be revoked at any time.
Endpoints
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/instances | instances:read |
| POST | /api/v1/instances/{id}/messages | messages:write |
| GET | /api/v1/instances/{id}/runs/{runId} | runs:read |
| GET | /api/v1/usage?days=30 | usage:read |
Typical flow
Send a message (answers 202 with the run id) and poll the run until its status is done or failed. Or subscribe a webhook and we will tell you.
curl -X POST https://agneos.com/api/v1/instances/$INSTANCE/messages \
-H "Authorization: Bearer $AGNOSE_API_KEY" \
-H "content-type: application/json" \
-d '{"text": "Hola"}'
# 202 {"id": "run_…", "status": "queued", …}
curl https://agneos.com/api/v1/instances/$INSTANCE/runs/$RUN \
-H "Authorization: Bearer $AGNOSE_API_KEY"
# 200 {"status": "done", "reply": "…"}MCP server
Agnose speaks MCP over HTTP (streamable HTTP). Tools: list_agents, send_message and get_run. To pin it to one instance use /api/mcp/<instance id>.
{
"mcpServers": {
"agnose": {
"type": "http",
"url": "https://agneos.com/api/mcp",
"headers": {
"Authorization": "Bearer ${AGNOSE_API_KEY}"
}
}
}
}Webhooks
We send a signed POST when a run finishes (run.completed), fails (run.failed) or you cancel an instance (instance.cancelled). If your server does not answer 2xx we retry with growing delays, up to 8 times.
Verifying the signature
The agnose-signature header carries t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.body"> with your secret. Reject signatures older than 5 minutes.
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(secret, body, header) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const want = createHmac('sha256', secret).update(`${t}.${body}`).digest('hex');
return v1.length === want.length && timingSafeEqual(Buffer.from(v1), Buffer.from(want));
}Limits
60 calls per minute per key and 120 per IP. Past that we answer 429 with Retry-After. Each plan's daily message limits apply too.
Errors
Errors come as {"error": {"code", "message"}}: unauthorized (401), forbidden (403), not_found (404), invalid (400), plan_limit and rate_limited (429).