Agent API
Early
This API is used by our own agent instances today. It will change; we will version it before a third party depends on it.
Base URL: https://app.olsie.ai. Every call carries a tenant agent token: Authorization: Bearer olsie_…. Tokens are minted per workspace and stored hashed; a token can only touch its own workspace.
POST /api/agent/ingest
An agent reports what it did. Idempotent on (agent, externalId).
{
"agent": "gro",
"events": [
{ "type": "heartbeat", "channelLabel": "Discord #growth-ops" },
{ "type": "approval", "externalId": "a1b2c3d4", "title": "google_ads_mutate: add 12 negatives", "status": "pending", "detail": { "input": { } } },
{ "type": "job", "externalId": "weekly-digest-2026-09-05T08:30", "kind": "scheduled", "summary": "Weekly digest posted", "status": "done", "costPence": 34 }
]
}
Event types: heartbeat (marks the agent active), approval (pending | approved | declined | expired), job (running | done | failed, with optional tokensIn, tokensOut, costPence, startedAt, finishedAt). Everything lands in the audit trail.
POST /api/agent/secret
{ "name": "CLOUDFLARE_TOKEN", "agent": "heimdall" }
Returns { "ok": true, "value": "…" } for the calling workspace only, and records secret.used in the audit. This is the only path by which a value leaves the vault, and it is called by a tool, never by the model.
GET /api/agent/approvals?agent=gro
Decisions made in the pane that the agent has not yet acted on:
{ "ok": true, "decisions": [ { "id": "…", "externalId": "a1b2c3d4", "status": "approved", "decidedAt": "…" } ] }
The agent treats approved exactly like a ✅ in its channel, then acknowledges with POST /api/agent/approvals { "ids": ["…"] }.
Errors
401 bad or missing token · 400 malformed body · 404 secret not set. Bodies are { "ok": false, "error": "…" }.