Basics
Base URL https://toolcaise.com. Every agent request carries the agent's credential as Authorization: Bearer tca1...; it identifies the workspace and the agent, so neither is ever sent in a body. Create one per agent in the dashboard under Agents; it is shown once and can be rotated or revoked there. Requests are outbound HTTPS from the agent; nothing connects to your hosts.
| Endpoint | Body limit | Rate limit |
|---|---|---|
/api/v1/agents/telemetry | 128,000 bytes | 60 a minute per credential, shared with metrics and supervision |
/api/v1/agents/metrics | 64,000 bytes | Shared, as above |
/api/v1/agents/supervision | 16,000 bytes | Shared, as above |
/api/v1/agents/control | 24,576 bytes | Its own budget, so reporting can never starve a pause or cancel |
/api/v1/otlp/v1/traces | 4 MiB, 16 MiB after gzip | Its own budget |
JSON bodies need Content-Type: application/json. Errors are JSON objects with one field, error, that says what was wrong. Retry network failures, 429 and 503 after the Retry-After delay with the same IDs: runs, spans, deliveries and checks are updated in place and events, outcomes and samples are stored once, so a retry never duplicates anything. Timestamps are ISO 8601 with a time zone.
Everything an agent sends is stored as agent-reported: a claim, not independent proof. Records sent with a credential Toolcaise Connect enrolled for a supervised run or MCP gateway are stored as measured by Connect instead; the label follows the credential, never a field in the request. Send operational metadata only. Fields named like secrets, prompts or content are refused; free text such as event messages is stored as written.
Reporting
What the agent did.
POST/api/v1/agents/telemetry
Report a heartbeat, run, events, spans or outcomes
One JSON report of up to 128,000 bytes. It is all or nothing: one invalid entry refuses the whole report with 400. Spans and outcomes must name a run this agent has reported, in the same request or an earlier one. Retrying with the same IDs is safe: events and outcomes are stored once, runs and deliveries are updated. Telemetry, metrics and supervision requests share 60 requests a minute per credential; beyond that the answer is 429 with Retry-After: 60.
| Field | Type | Details | |
|---|---|---|---|
version | 1 | required | |
status | "healthy" | "degraded" | "offline" | "disabled" | optional | The agent's own view of its health. A report without it says healthy. Default "healthy". |
heartbeatAt | string (date-time) | optional | Defaults to the time Toolcaise receives the report. At most 10 minutes old and at most 1 minute ahead. |
hostName | string | optional | Where the agent runs. A report without it records "Unreported". 1 to 160 characters. |
unitName | string or null | optional | The service or unit name. 1 to 200 characters. |
releaseVersion | string or null | optional | The agent's release. 1 to 120 characters. |
capabilities | array of string | optional | Descriptive labels. They grant nothing. At most 24 items. |
schedule | Schedule or null | optional | The schedule the agent runs on, so an overdue run can be flagged. |
schedule.name | string | required | 1 to 120 characters. |
schedule.calendar | string | required | For example a cron expression or "daily 06:00". 1 to 180 characters. |
schedule.timezone | string | required | An IANA time zone. 1 to 80 characters. |
schedule.enabled | boolean | optional | |
schedule.active | boolean | optional | |
schedule.nextRunAt | string (date-time) or null | optional | The next expected run. |
schedule.lastTriggeredAt | string (date-time) or null | optional | The last time it fired. |
run | Run or null | optional | A snapshot of one run. Send it at the start (status running) and again when it ends; later snapshots update the same run. |
run.externalRunId | string | required | Your ID for the run; reuse it in every report about the run. 2 to 120 letters, digits, dots, underscores, colons or hyphens. |
run.status | "running" | "completed" | "failed" | "held" | "skipped" | "shadow_passed" | "canceled" | required | |
run.mode | string | required | How the run executes, for example "observed". 1 to 40 characters. |
run.trigger | string | required | What started it, for example "application" or "schedule". 1 to 40 characters. |
run.startedAt | string (date-time) | required | When the run started. Historical values are accepted. |
run.completedAt | string (date-time) or null | optional | When the run ended. |
run.phase | string or null | optional | 1 to 120 characters. |
run.releaseVersion | string or null | optional | 1 to 120 characters. |
run.summary | object | optional | Short structured facts about the run, such as a label. At most 12,000 bytes as JSON. A key anywhere inside that contains secret, token, password, authorization, cookie, credential, prompt, html, content, extract, body or private key is refused. |
run.counts | object | optional | Named counters. Keys follow the external ID format; values are integers from 0 to 1,000,000. |
run.errorCode | string or null | optional | 1 to 120 characters. |
run.artifactDigest | string or null | optional | |
run.durationMs | integer or null | optional | 0 to 86,400,000. |
events | array of Event | optional | At most 100 items. |
events[].externalEventId | string | required | Your ID for the event; a repeated ID is stored once. 2 to 120 letters, digits, dots, underscores, colons or hyphens. |
events[].externalRunId | string or null | optional | The run it belongs to, if any. 2 to 120 letters, digits, dots, underscores, colons or hyphens. |
events[].eventType | string | required | A dotted type such as report.sent, in the external ID format. Types starting toolcaise. are reserved. |
events[].level | "info" | "warning" | "error" | required | |
events[].message | string | required | A short, safe description. Never prompts, output or personal data. 1 to 600 characters. |
events[].phase | string or null | optional | 1 to 120 characters. |
events[].detail | object | optional | Structured detail. At most 12,000 bytes as JSON. A key anywhere inside that contains secret, token, password, authorization, cookie, credential, prompt, html, content, extract, body or private key is refused. |
events[].occurredAt | string (date-time) or null | optional | Defaults to the time Toolcaise receives it. |
deliveries | array of Delivery | optional | At most 24 items. |
deliveries[].externalDeliveryId | string | required | Your ID for the delivery; later reports update it. 2 to 120 letters, digits, dots, underscores, colons or hyphens. |
deliveries[].externalRunId | string or null | optional | The run it belongs to, if any. 2 to 120 letters, digits, dots, underscores, colons or hyphens. |
deliveries[].channel | string | required | For example email or webhook. 2 to 120 letters, digits, dots, underscores, colons or hyphens. |
deliveries[].status | "pending" | "sent" | "failed" | "skipped" | required | |
deliveries[].recipientLabel | string or null | optional | 1 to 160 characters. |
deliveries[].errorCode | string or null | optional | 1 to 120 characters. |
deliveries[].attemptedAt | string (date-time) or null | optional | Defaults to the time Toolcaise receives it. |
deliveries[].sentAt | string (date-time) or null | optional | When it was sent. |
deliveries[].detail | object | optional | Structured detail. At most 12,000 bytes as JSON. A key anywhere inside that contains secret, token, password, authorization, cookie, credential, prompt, html, content, extract, body or private key is refused. |
spans | array of Span | optional | At most 50 items. |
spans[].externalRunId | string | required | A run this agent has reported, in this request or earlier. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
spans[].externalSpanId | string | required | Your ID for the span. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
spans[].parentSpanId | string | optional | The enclosing span, if any. A span cannot parent itself. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
spans[].name | string | required | What the step is. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
spans[].kind | "step" | "tool" | "model" | "retry" | required | |
spans[].status | "running" | "succeeded" | "failed" | "cancelled" | required | |
spans[].startedAt | string (date-time) | required | At most one minute in the future. |
spans[].completedAt | string (date-time) | optional | Not before startedAt; at most one minute in the future. |
spans[].tool | string | optional | The tool used. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
spans[].model | string | optional | The model used. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
spans[].inputTokens | integer | optional | 0 to 1,000,000,000. |
spans[].outputTokens | integer | optional | 0 to 1,000,000,000. |
spans[].reportedCostUsd | number | optional | 0 to 1,000,000,000. |
spans[].metadata | object | optional | Up to 16 numbers or booleans. Keys must match ^[a-zA-Z][a-zA-Z0-9_]{0,39}$; keys containing secret, token, password, key, prompt, input, output, body or content, and values of other types, are dropped rather than refused. At most 16 entries. |
outcomes | array of Outcome | optional | At most 20 items. |
outcomes[].externalRunId | string | required | A run this agent has reported, in this request or earlier. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
outcomes[].externalOutcomeId | string | required | Your ID for the outcome; a repeated ID is stored once. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
outcomes[].label | string | required | What was produced. No line breaks or control characters; credential-like strings and email addresses are replaced with [redacted]. 1 to 160 characters. |
outcomes[].reportedStatus | "succeeded" | "failed" | "unknown" | required | |
outcomes[].artifactDigest | string | optional | SHA-256 of the artifact. |
Refused with 400: orgId, agentId, agent.
| Status | Meaning |
|---|---|
200 | Stored. |
400 | The body is not valid for this endpoint; error says which field. |
401 | Missing, malformed or revoked agent credential. |
413 | The body is larger than 128,000 bytes. |
415 | Content-Type is not application/json. |
429 | Telemetry, metrics and supervision requests share 60 requests a minute per credential; beyond that the answer is 429 with Retry-After: 60. |
503 | Storage is temporarily unavailable. Retry after the Retry-After delay with the same IDs. |
{
"version": 1,
"status": "healthy",
"hostName": "worker-3",
"releaseVersion": "2026.09.1",
"run": {
"externalRunId": "run-2026-09-22-0600",
"status": "completed",
"mode": "observed",
"trigger": "schedule",
"startedAt": "2026-09-22T06:00:00Z",
"completedAt": "2026-09-22T06:04:12Z",
"durationMs": 252000,
"summary": {
"label": "Daily research digest"
},
"counts": {
"sources": 14
}
},
"events": [
{
"externalEventId": "run-2026-09-22-0600:sent",
"externalRunId": "run-2026-09-22-0600",
"eventType": "report.sent",
"level": "info",
"message": "Digest sent to the research channel",
"occurredAt": "2026-09-22T06:04:10Z"
}
],
"spans": [
{
"externalRunId": "run-2026-09-22-0600",
"externalSpanId": "summarize-1",
"name": "Summarize sources",
"kind": "model",
"status": "succeeded",
"startedAt": "2026-09-22T06:01:02Z",
"completedAt": "2026-09-22T06:01:30Z",
"model": "gpt-4.1",
"inputTokens": 1200,
"outputTokens": 350,
"reportedCostUsd": 0.012,
"metadata": {
"latencyMs": 28000,
"streamed": false
}
}
],
"outcomes": [
{
"externalRunId": "run-2026-09-22-0600",
"externalOutcomeId": "digest",
"label": "Daily digest",
"reportedStatus": "succeeded"
}
]
}{
"version": 1
}curl --fail-with-body https://toolcaise.com/api/v1/agents/telemetry \
-H "Authorization: Bearer $TOOLCAISE_AGENT_TOKEN" \
-H "Content-Type: application/json" \
--data '{"version":1,"status":"healthy","hostName":"worker-3","releaseVersion":"2026.09.1","run":{"externalRunId":"run-2026-09-22-0600","status":"completed","mode":"observed","trigger":"schedule","startedAt":"2026-09-22T06:00:00Z","completedAt":"2026-09-22T06:04:12Z","durationMs":252000,"summary":{"label":"Daily research digest"},"counts":{"sources":14}},"events":[{"externalEventId":"run-2026-09-22-0600:sent","externalRunId":"run-2026-09-22-0600","eventType":"report.sent","level":"info","message":"Digest sent to the research channel","occurredAt":"2026-09-22T06:04:10Z"}],"spans":[{"externalRunId":"run-2026-09-22-0600","externalSpanId":"summarize-1","name":"Summarize sources","kind":"model","status":"succeeded","startedAt":"2026-09-22T06:01:02Z","completedAt":"2026-09-22T06:01:30Z","model":"gpt-4.1","inputTokens":1200,"outputTokens":350,"reportedCostUsd":0.012,"metadata":{"latencyMs":28000,"streamed":false}}],"outcomes":[{"externalRunId":"run-2026-09-22-0600","externalOutcomeId":"digest","label":"Daily digest","reportedStatus":"succeeded"}]}'POST/api/v1/agents/metrics
Report usage and resource samples, and output checks
Up to 30 samples and 30 output checks per request, 64,000 bytes at most. Unknown top-level fields are refused. Samples and checks that name a run need that run reported first. Call-scope samples are per model call and run-scope samples are cumulative totals, so the two are never added together. Telemetry, metrics and supervision requests share 60 requests a minute per credential; beyond that the answer is 429 with Retry-After: 60.
| Field | Type | Details | |
|---|---|---|---|
version | 1 | required | |
samples | array of MetricSample | optional | At most 30 items. |
samples[].externalSampleId | string | required | Your ID for the sample; a repeated ID is stored once. Up to 200 characters, no control characters or credentials. 1 to 200 characters. |
samples[].externalRunId | string or null | optional | A run this agent has reported. Required for run and call scope. Up to 200 characters, no control characters or credentials. 1 to 200 characters. |
samples[].scope | "host" | "run" | "call" | required | host: the machine or process. run: cumulative totals for a run. call: one model call, identified by callId. |
samples[].callId | string or null | optional | Required for call scope; reuse it when retrying. Up to 200 characters, no control characters or credentials. 1 to 200 characters. |
samples[].recordedAt | string (date-time) | required | At most one minute ahead and no older than 31 days. |
samples[].tags | object | optional | |
samples[].tags.version | string | optional | Your agent or prompt version. Up to 100 characters, no control characters or credentials. 1 to 100 characters. |
samples[].tags.prompt | string | optional | A prompt template name, never the prompt. Up to 100 characters, no control characters or credentials. 1 to 100 characters. |
samples[].tags.model | string | optional | The model. Up to 100 characters, no control characters or credentials. 1 to 100 characters. |
samples[].values | object | required | At least one measurement. null means unknown, never zero. |
samples[].values.contextTokens | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.contextCapacity | integer or null | optional | 0 to 1,000,000,000,000,000. More than 0. |
samples[].values.inputTokens | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.outputTokens | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.reportedCostUsd | number or null | optional | 0 to 1,000,000,000. |
samples[].values.ramBytes | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.cpuPercent | number or null | optional | 0 to 100. |
samples[].values.diskFreeBytes | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.diskUsedBytes | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.outputBytes | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.artifactBytes | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.workloadItems | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.runtimeMs | number or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.retryCount | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.stepCount | integer or null | optional | 0 to 1,000,000,000,000,000. |
samples[].values.childCount | integer or null | optional | 0 to 1,000,000,000,000,000. |
checks | array of OutputCheck | optional | At most 30 items. |
checks[].externalCheckId | string | required | Your ID for the check; a newer result replaces an older one. Up to 200 characters, no control characters or credentials. 1 to 200 characters. |
checks[].externalRunId | string or null | optional | A run this agent has reported. Up to 200 characters, no control characters or credentials. 1 to 200 characters. |
checks[].name | string | required | What was checked. Up to 160 characters, no control characters or credentials. 1 to 160 characters. |
checks[].kind | "exists" | "nonempty" | "schema" | "freshness" | required | |
checks[].passed | boolean or null | required | null means the check could not tell. |
checks[].recordedAt | string (date-time) | required | At most one minute ahead. |
checks[].reason | "missing" | "empty" | "invalid_json" | "schema_mismatch" | "stale" | "unreadable" | "too_large" | "not_regular_file" | "unsupported_schema" | "verified" or null | optional |
| Status | Meaning |
|---|---|
200 | Stored. |
400 | The body is not valid for this endpoint; error says which field. |
401 | Missing, malformed or revoked agent credential. |
413 | The body is larger than 64,000 bytes. |
415 | Content-Type is not application/json. |
429 | Telemetry, metrics and supervision requests share 60 requests a minute per credential; beyond that the answer is 429 with Retry-After: 60. |
503 | Storage is temporarily unavailable. Retry with the same sample IDs. |
{
"version": 1,
"samples": [
{
"externalSampleId": "run-2026-09-22-0600:summarize-1:usage",
"externalRunId": "run-2026-09-22-0600",
"scope": "call",
"callId": "summarize-1",
"recordedAt": "2026-09-22T06:01:30Z",
"tags": {
"model": "gpt-4.1",
"prompt": "summary-v3"
},
"values": {
"inputTokens": 1200,
"outputTokens": 350,
"reportedCostUsd": 0.012
}
}
],
"checks": [
{
"externalCheckId": "daily-report:exists",
"externalRunId": "run-2026-09-22-0600",
"name": "Daily report",
"kind": "exists",
"passed": true,
"reason": "verified",
"recordedAt": "2026-09-22T06:04:00Z"
}
]
}curl --fail-with-body https://toolcaise.com/api/v1/agents/metrics \
-H "Authorization: Bearer $TOOLCAISE_AGENT_TOKEN" \
-H "Content-Type: application/json" \
--data '{"version":1,"samples":[{"externalSampleId":"run-2026-09-22-0600:summarize-1:usage","externalRunId":"run-2026-09-22-0600","scope":"call","callId":"summarize-1","recordedAt":"2026-09-22T06:01:30Z","tags":{"model":"gpt-4.1","prompt":"summary-v3"},"values":{"inputTokens":1200,"outputTokens":350,"reportedCostUsd":0.012}}],"checks":[{"externalCheckId":"daily-report:exists","externalRunId":"run-2026-09-22-0600","name":"Daily report","kind":"exists","passed":true,"reason":"verified","recordedAt":"2026-09-22T06:04:00Z"}]}'Controls
Decisions the agent waits for. They fail closed.
POST/api/v1/agents/control
Poll for commands, acknowledge them, and request and use approvals
Cooperative controls: the agent asks at checkpoints it chooses, so nothing here can interrupt work already in progress. Any failure means do not proceed: an unreachable server, an error, or an uncertain consume_approval response must leave the guarded action unexecuted. Bodies are at most 24,576 bytes. Control has its own limit of 300 requests a minute per credential per server process, so a busy reporter cannot starve a pause or a cancel.
op: "poll"
Ask for commands at a checkpoint, declaring what this runner can apply; only those commands are delivered. With runId, the run must already be reported.
| Field | Type | Details | |
|---|---|---|---|
op | "poll" | required | |
capabilities | array of "pause" | "resume" | "cancel" | "approvals" | "policy" | required | At most 5 items. |
runId | string or null | optional | The run at this checkpoint. Up to 200 characters, no control characters. 1 to 200 characters. |
policyVersion | integer or null | optional | With the policy capability: the policy version this runner enforces now. 1 to 2,000,000,000. |
policyRejected | object or null | optional | |
policyRejected.version | integer | required | 1 to 2,000,000,000. |
policyRejected.reason | string | required | Why the runner could not apply it. Up to 200 characters, no control characters. 1 to 200 characters. |
op: "ack"
Confirm that a delivered command was applied.
| Field | Type | Details | |
|---|---|---|---|
op | "ack" | required | |
commandId | string (uuid) | required | |
appliedState | "paused" | "running" | "cancelled" | required | paused for pause, running for resume, cancelled for cancel. |
op: "request_approval"
Ask a person to approve one exact action. Toolcaise stores the digest and the title, never the action itself; the optional reason, destination, affectedCount and permittedFields tell the person more. Those are shown as reported by the agent: Toolcaise cannot check them against the digest. In every text, credential-like strings (bearer tokens, API keys, private keys, password= pairs) and email addresses are replaced with [redacted], and a permitted field whose name suggests a secret (password, token, credential, cookie, or a name ending in key, such as api_key or sshKey) shows [redacted] as its value. Repeating a requestId returns the same approval; repeating it with anything different is refused with 409.
| Field | Type | Details | |
|---|---|---|---|
op | "request_approval" | required | |
runId | string | required | A run this agent has reported. Up to 200 characters, no control characters. 1 to 200 characters. |
actionDigest | string | required | SHA-256 of the canonical JSON of the exact action. Lowercase hexadecimal SHA-256. |
requestId | string (uuid) | required | |
title | string | required | What the person is approving, safe to show. Up to 160 characters, no control characters. 1 to 160 characters. |
expiresInSeconds | integer | optional | 60 to 86,400. Default 3600. |
reason | string or null | optional | Why a person must approve it, for example the policy rule that matched. Up to 200 characters, no control characters. 1 to 200 characters. |
destination | string or null | optional | The system, host or record type the action writes to. Up to 120 characters, no control characters. 1 to 120 characters. |
affectedCount | integer or null | optional | How many records, messages or people the action affects, as the agent counts them. 0 to 1,000,000,000. |
permittedFields | array of object or null | optional | The action's parameters the agent chooses to show the person, as text. Send only what the decision needs; the rest of the action never leaves the agent. At most 12 items. |
permittedFields.name | string | required | The parameter's name. Up to 40 characters, no control characters. 1 to 40 characters. |
permittedFields.value | string | required | The parameter's value as text, up to 200 characters, no control characters. Up to 200 characters. |
op: "approval_status"
Read the state of an approval.
| Field | Type | Details | |
|---|---|---|---|
op | "approval_status" | required | |
runId | string | required | The run the approval is bound to. Up to 200 characters, no control characters. 1 to 200 characters. |
actionDigest | string | required | The digest the approval is bound to. Lowercase hexadecimal SHA-256. |
approvalId | string (uuid) | required |
op: "consume_approval"
Use an approval, once, immediately before executing the action. Refused while the agent or run is paused or cancelled. Never retry an uncertain response: treat it as not authorized.
| Field | Type | Details | |
|---|---|---|---|
op | "consume_approval" | required | |
runId | string | required | The run the approval is bound to. Up to 200 characters, no control characters. 1 to 200 characters. |
actionDigest | string | required | The digest the approval is bound to. Lowercase hexadecimal SHA-256. |
approvalId | string (uuid) | required |
Refused with 400: orgId, agentId.
| Status | Meaning |
|---|---|
200 | Done. |
400 | The body is not valid for this endpoint; error says which field. |
401 | Missing, malformed or revoked agent credential. |
404 | The run has not been reported yet, or the command or approval does not exist for this agent. |
409 | Not allowed in the current state: approval support not declared at a checkpoint, the approval is bound to another action or is pending, denied, expired or used, the agent or run is paused or cancelled, or 100 approvals are already pending. |
413 | The body is larger than 24,576 bytes. |
415 | Content-Type is not application/json. |
429 | More than 300 control requests a minute for this credential. Retry-After says how long to wait. |
503 | Control storage is unavailable. Do not execute unapproved actions. |
{
"op": "poll",
"runId": "run-2026-09-22-0600",
"capabilities": [
"pause",
"resume",
"cancel",
"approvals"
]
}{
"op": "ack",
"commandId": "5f0f3c1e-8a1b-4c2d-9e3f-0a1b2c3d4e5f",
"appliedState": "paused"
}{
"op": "request_approval",
"runId": "run-2026-09-22-0600",
"actionDigest": "9f2c7a0e4b1d3c5a6e8f0b2d4c6a8e0f1b3d5c7a9e1f3b5d7c9a1e3f5b7d9c1a",
"requestId": "1b4e28ba-2fa1-41d2-883f-0016d3cca427",
"title": "Send the digest to 1,240 subscribers",
"expiresInSeconds": 3600,
"reason": "Sends to more than 1,000 people need a person's approval",
"destination": "Newsletter list: subscribers",
"affectedCount": 1240,
"permittedFields": [
{
"name": "list",
"value": "subscribers"
},
{
"name": "draft",
"value": "digest-2026-09-22"
}
]
}{
"op": "approval_status",
"runId": "run-2026-09-22-0600",
"actionDigest": "9f2c7a0e4b1d3c5a6e8f0b2d4c6a8e0f1b3d5c7a9e1f3b5d7c9a1e3f5b7d9c1a",
"approvalId": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}{
"op": "consume_approval",
"runId": "run-2026-09-22-0600",
"actionDigest": "9f2c7a0e4b1d3c5a6e8f0b2d4c6a8e0f1b3d5c7a9e1f3b5d7c9a1e3f5b7d9c1a",
"approvalId": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}curl --fail-with-body https://toolcaise.com/api/v1/agents/control \
-H "Authorization: Bearer $TOOLCAISE_AGENT_TOKEN" \
-H "Content-Type: application/json" \
--data '{"op":"poll","runId":"run-2026-09-22-0600","capabilities":["pause","resume","cancel","approvals"]}'Supervision workflows
Recovery, impact, sources and handoffs.
GET/api/v1/agents/supervision
Read this agent's workflow items
Telemetry, metrics and supervision requests share 60 requests a minute per credential; beyond that the answer is 429 with Retry-After: 60.
| Status | Meaning |
|---|---|
200 | The snapshot. |
401 | Missing, malformed or revoked agent credential. |
429 | Telemetry, metrics and supervision requests share 60 requests a minute per credential; beyond that the answer is 429 with Retry-After: 60. |
503 | Supervision storage is unavailable. |
POST/api/v1/agents/supervision
Report and advance workflow items
The operations an agent may perform. Reviews, recovery authorizations, assignments, source invalidation and correction, effort, offboarding and drills are human operations and are refused with 403 for an agent credential. Items carry a version: send it back as expectedVersion; a 409 means the item changed. Bodies are at most 16,000 bytes. Telemetry, metrics and supervision requests share 60 requests a minute per credential; beyond that the answer is 429 with Retry-After: 60.
op: "create"
Report a workflow item. recovery_step, impact and output need a reported run. Repeating a requestId returns the existing item.
| Field | Type | Details | |
|---|---|---|---|
op | "create" | required | |
kind | "recovery_step" | "impact" | "obligation" | "source" | "output" | required | |
runId | string or null | optional | A run this agent has reported. Up to 120 characters, no control characters, credentials or email addresses. 1 to 120 characters. |
title | string | required | What the item is. Up to 160 characters, no control characters, credentials or email addresses. 1 to 160 characters. |
requestId | string (uuid) | required | |
data | object | optional | Depends on kind: see the matching schema below. |
data when kind is recovery_step (runId required)
| Field | Type | Details | |
|---|---|---|---|
stepKey | string | required | Which step of the run to recover. Up to 80 characters, no control characters, credentials or email addresses. 1 to 80 characters. |
actionDigest | string | required | The exact recovery action. Lowercase hexadecimal SHA-256. |
evidenceDigest | string or null | optional | Evidence gathered for review. Lowercase hexadecimal SHA-256. |
proposedClassification | "verified_complete" | "safe_to_retry" | "needs_approval" | "cannot_verify" | optional | Default "cannot_verify". |
effect | "none" | "read" | "write" | "unknown" | optional | Default "unknown". |
data when kind is impact (runId required)
| Field | Type | Details | |
|---|---|---|---|
actionDigest | string | required | The action whose impact this is. Lowercase hexadecimal SHA-256. |
before | object | optional | Up to 20 numbers or booleans. Names containing secret, token, password, email, prompt, content, payload or credential are refused. At most 20 entries. |
after | object | optional | Up to 20 numbers or booleans. Names containing secret, token, password, email, prompt, content, payload or credential are refused. At most 20 entries. |
affectedCount | integer | optional | 0 to 1,000,000,000. Default 0. |
reversibility | "reversible" | "partial" | "irreversible" | "unknown" | optional | Default "unknown". |
dependencies | array of string (uuid) | optional | At most 24 items. |
data when kind is output (runId required)
| Field | Type | Details | |
|---|---|---|---|
artifactDigest | string | required | The artifact produced. Lowercase hexadecimal SHA-256. |
outcomeId | string (uuid) or null | optional | A reported outcome of the same run. |
data when kind is source
| Field | Type | Details | |
|---|---|---|---|
sourceKey | string | required | Which source. Up to 160 characters, no control characters, credentials or email addresses. 1 to 160 characters. |
contentDigest | string | required | The content the agent used. Lowercase hexadecimal SHA-256. |
data when kind is obligation
| Field | Type | Details | |
|---|---|---|---|
dueAt | string (date-time) or null | optional | |
handoffCode | string | optional | What happens next. Up to 160 characters, no control characters, credentials or email addresses. 1 to 160 characters. Default "review_required". |
op: "claim_recovery"
Claim a recovery step before running it: one a person authorized, or one whose effect is read or none, which needs no authorization. Single use. With an idempotencyKey, repeating the same request returns the original claim instead of a second grant, so an uncertain response can be retried with the same key; without one, never retry an uncertain response.
| Field | Type | Details | |
|---|---|---|---|
op | "claim_recovery" | required | |
id | string (uuid) | required | |
expectedVersion | integer | required | 0 to 1,000,000,000. |
actionDigest | string | required | The exact action that was authorized. Lowercase hexadecimal SHA-256. |
idempotencyKey | string | optional | Chosen by the runner, and kept until the result is reported: a UUID, or letters, digits and . _ : -. Unique per agent. The same key with the same id and actionDigest returns the same claim while it is open, with expectedVersion either the version claimed or the claim's itemVersion. A key that looks like a credential (a word starting tca1., sk_live_, sk_test_, or sk- and 12 letters or digits) is refused. 8 to 128 characters. |
op: "recovery_result"
| Field | Type | Details | |
|---|---|---|---|
op | "recovery_result" | required | |
id | string (uuid) | required | |
expectedVersion | integer | required | 0 to 1,000,000,000. |
result | "succeeded" | "failed" | required | |
evidenceDigest | string or null | optional | Evidence of the result. Lowercase hexadecimal SHA-256. |
op: "observe_job"
Report what a job actually used, to compare with what its definition allows.
| Field | Type | Details | |
|---|---|---|---|
op | "observe_job" | required | |
jobId | string (uuid) | required | |
requestId | string (uuid) | required | |
tools | array of string | optional | Tools used. At most 24 items. |
systems | array of string | optional | Systems touched. At most 24 items. |
actions | array of string | optional | Actions taken. At most 24 items. |
runId | string or null | optional | The run. Up to 120 characters, no control characters, credentials or email addresses. 1 to 120 characters. |
op: "link"
| Field | Type | Details | |
|---|---|---|---|
op | "link" | required | |
fromId | string (uuid) | required | |
toId | string (uuid) | required | |
relation | "derived_from" | "depends_on" | "handoff" | required |
op: "report_handoff"
Report progress on an obligation assigned to this agent.
| Field | Type | Details | |
|---|---|---|---|
op | "report_handoff" | required | |
id | string (uuid) | required | |
expectedVersion | integer | required | 0 to 1,000,000,000. |
state | "in_progress" | "reported_complete" | "blocked" | required | |
evidenceDigest | string or null | optional | Evidence of the progress. Lowercase hexadecimal SHA-256. |
Refused with 400: agentId, orgId, actor.
| Status | Meaning |
|---|---|
200 | Done. |
400 | The body is not valid for this endpoint; error says which field. |
401 | Missing, malformed or revoked agent credential. |
403 | A human operation, or a job or offboarding item, sent by an agent. |
404 | The item, outcome or assigned handoff does not belong to this agent. |
409 | The item changed (stale expectedVersion), or the operation is not allowed in its state. |
413 | The body is larger than 16,000 bytes. |
415 | Content-Type is not application/json. |
429 | Telemetry, metrics and supervision requests share 60 requests a minute per credential; beyond that the answer is 429 with Retry-After: 60. |
503 | Supervision storage is unavailable. Do not execute an unconfirmed recovery. |
{
"op": "create",
"kind": "recovery_step",
"runId": "run-2026-09-22-0600",
"title": "Resend the digest to the failed batch",
"requestId": "a8098c1a-f86e-41d2-a0a3-b3c4d5e6f708",
"data": {
"stepKey": "send-batch-7",
"actionDigest": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b",
"proposedClassification": "safe_to_retry",
"effect": "write"
}
}{
"op": "claim_recovery",
"id": "0b4c2f1e-3d5a-4b6c-8d7e-9f0a1b2c3d4e",
"expectedVersion": 3,
"actionDigest": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b",
"idempotencyKey": "0f8fad5b-d9cb-469f-a165-70867728950e"
}{
"op": "recovery_result",
"id": "0b4c2f1e-3d5a-4b6c-8d7e-9f0a1b2c3d4e",
"expectedVersion": 4,
"result": "succeeded"
}{
"op": "observe_job",
"jobId": "c56a4180-65aa-42ec-a945-5fd21dec0538",
"requestId": "e4eaaaf2-d142-41c8-9f0e-0a1b2c3d4e5f",
"tools": [
"web_search",
"send_email"
],
"systems": [
"newsletter"
],
"actions": [
"send"
],
"runId": "run-2026-09-22-0600"
}{
"op": "link",
"fromId": "0b4c2f1e-3d5a-4b6c-8d7e-9f0a1b2c3d4e",
"toId": "c56a4180-65aa-42ec-a945-5fd21dec0538",
"relation": "depends_on"
}{
"op": "report_handoff",
"id": "0b4c2f1e-3d5a-4b6c-8d7e-9f0a1b2c3d4e",
"expectedVersion": 2,
"state": "in_progress"
}curl --fail-with-body https://toolcaise.com/api/v1/agents/supervision \
-H "Authorization: Bearer $TOOLCAISE_AGENT_TOKEN" \
-H "Content-Type: application/json" \
--data '{"op":"create","kind":"recovery_step","runId":"run-2026-09-22-0600","title":"Resend the digest to the failed batch","requestId":"a8098c1a-f86e-41d2-a0a3-b3c4d5e6f708","data":{"stepKey":"send-batch-7","actionDigest":"3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b","proposedClassification":"safe_to_retry","effect":"write"}}'OpenTelemetry
OTLP/HTTP trace export.
POST/api/v1/otlp/v1/traces
Export OpenTelemetry traces (OTLP/HTTP)
Point an OpenTelemetry trace exporter here (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) with the agent credential in OTEL_EXPORTER_OTLP_HEADERS. Each trace becomes a run; model, tool and agent-step spans are stored with the token counts the instrumentation reported. Only an allow-list of GenAI, OpenInference, OpenLLMetry and Vercel AI SDK attributes is read, so prompts, completions and tool arguments are never decoded. Bodies are at most 4 MiB as sent and 16 MiB after gzip, and at most 2000 spans are stored per request. 600 requests a minute per credential per server process, separate from the JSON API's 60. Observation only: this path cannot pause, cancel or approve.
Body: an OTLP ExportTraceServiceRequest, as application/x-protobuf or application/json, optionally gzip compressed. See the framework quickstarts for exporter settings.
| Status | Meaning |
|---|---|
200 | Accepted. JSON requests get JSON back and protobuf requests get protobuf; partialSuccess counts spans that were not stored. |
400 | Malformed protobuf, JSON or gzip. Exporters do not retry this. |
401 | Missing, malformed or revoked agent credential. |
413 | The body is too large. Lower the exporter's batch size. |
415 | Use application/x-protobuf or application/json, with gzip or no encoding. |
429 | Too many export requests for this credential. Retry-After says how long to wait. |
503 | Storage is unavailable. Exporters retry; writes are idempotent. Retry-After says how long to wait. |
POST/api/v1/otlp/v1/metrics
Not supported: OpenTelemetry metrics
Toolcaise ingests traces only. Set OTEL_METRICS_EXPORTER=none. This answers 501 rather than accepting and discarding data.
| Status | Meaning |
|---|---|
501 | Metrics are not ingested. |
POST/api/v1/otlp/v1/logs
Not supported: OpenTelemetry logs
Toolcaise ingests traces only. Set OTEL_LOGS_EXPORTER=none. This answers 501 rather than accepting and discarding data.
| Status | Meaning |
|---|---|
501 | Logs are not ingested. |
Meta
This description.
GET/api/v1/openapi.json
This OpenAPI description
No credential needed.
| Status | Meaning |
|---|---|
200 | The OpenAPI 3.1 document. |