API Reference
Base URL: https://api.thothato.io
The Thoth-ATO REST API drives the remote console programmatically — useful for CI/CD pipelines, GitHub Actions, and orchestrators. All endpoints require a Bearer token.
Authentication
Section titled “Authentication”Authorization: Bearer sk_live_xxxxxAPI keys are issued from https://thothato.io/dashboard/api-keys. Keys are scoped to a single tenant. Format: sk_live_* (production) or sk_test_* (sandbox — no billing impact).
Errors
Section titled “Errors”The API returns standard HTTP status codes.
| Code | Meaning |
|---|---|
200 / 201 / 202 | Success. |
400 | Malformed request body or invalid parameters. |
401 | Missing or invalid Authorization header. |
403 | Key is valid but lacks permission for the resource (e.g. another tenant’s cycle). |
404 | Resource does not exist. |
409 | State conflict (e.g. cycle already terminated). |
422 | Validation failure on a structurally valid body. |
429 | Rate limit exceeded. See rate-limit headers below. |
5xx | Server error. Safe to retry with exponential backoff. |
Error body:
{ "error": { "code": "quota_exceeded", "message": "Signed-verdict quota reached; cycles continue unsigned. Upgrade for unlimited signed verdicts.", "docsUrl": "https://docs.thothato.io/pricing" }}Hitting the signed-verdict cap does not block the cycle. The cycle still runs to completion and returns an unsigned deliverable; only the cryptographic verdict signature is withheld until you upgrade.
Rate limits
Section titled “Rate limits”Every response includes:
X-RateLimit-Limit: 60X-RateLimit-Remaining: 58X-RateLimit-Reset: 1747400000X-RateLimit-Scope: api-keyLimit— requests permitted in the current windowRemaining— requests left in the current windowReset— Unix epoch seconds when the window resetsScope—api-key(per key) ortenant(aggregate)
When you hit the limit, the API returns 429 with a Retry-After header in seconds.
Cycles
Section titled “Cycles”POST /api/v1/cycles
Section titled “POST /api/v1/cycles”Start a new governed cycle.
Request body:
{ "goal": "Build a CLI that converts CSV to JSON, with tests.", "constitutionRef": "default", "specRef": "github://owner/repo/issues/42", "maxCycles": 3, "metadata": { "ci": "github-actions", "runId": "8472634123" }}| Field | Type | Required | Description |
|---|---|---|---|
goal | string | Yes (one of goal/specRef) | Natural-language goal. |
specRef | string | Yes (one of goal/specRef) | URI to a spec: github://..., https://..., or inline:<base64>. |
constitutionRef | string | No | Constitution to enforce. Default: default. |
maxCycles | int | No | Self-healing cap. Default: 3. Max: 10. |
metadata | object | No | Free-form caller-supplied metadata. Echoed back. Up to 4 KB. |
Response — 202 Accepted:
{ "cycleId": "cyc_01HXXXX", "status": "queued", "streamUrl": "/api/v1/cycles/cyc_01HXXXX/stream", "createdAt": "2026-05-16T12:00:00Z"}Errors:
401— missing/invalid key403— key lackscycles:write422— neithergoalnorspecRefsupplied, orspecRefURI invalid429— rate limit exceeded (the signed-verdict quota does not block cycle creation; the cycle runs and returns an unsigned deliverable)
GET /api/v1/cycles/{id}
Section titled “GET /api/v1/cycles/{id}”Fetch the current state of a cycle.
Response:
{ "cycleId": "cyc_01HXXXX", "status": "running", "phase": "execute", "createdAt": "2026-05-16T12:00:00Z", "completedAt": null, "constitutionRef": "default", "metadata": { "ci": "github-actions", "runId": "8472634123" }}status values: queued | running | completed | failed | rejected.
phase values: specify | plan | execute | verdict (only set while status=running).
rejected means the Judge refused to ship — the verdict explains which principle threshold was missed.
GET /api/v1/cycles/{id}/stream
Section titled “GET /api/v1/cycles/{id}/stream”Server-Sent Events (SSE) stream of cycle progress. Emits one event per phase transition and one per worker tool call. Connection stays open until the cycle terminates.
GET /api/v1/cycles/cyc_01HXXXX/stream HTTP/1.1Accept: text/event-streamAuthorization: Bearer sk_live_xxxxxEvent format:
event: phasedata: {"phase":"execute","worker":"thoth-develop","at":"2026-05-16T12:01:14Z"}
event: tool-calldata: {"worker":"thoth-develop","tool":"Write","target":"src/cli.ts"}
event: principle-scoredata: {"principle":"security","score":0.92}
event: verdictdata: {"cycleId":"cyc_01HXXXX","status":"completed"}The stream terminates with a final event: terminal payload carrying status and the deliverable URL.
GET /api/v1/cycles/{id}/deliverable
Section titled “GET /api/v1/cycles/{id}/deliverable”Fetch the final deliverable bundle once the cycle is completed. Returns 409 Conflict while still running.
Response:
{ "cycleId": "cyc_01HXXXX", "verdict": { "cycleId": "cyc_01HXXXX", "kmsKeyVersion": "projects/p/locations/l/keyRings/r/cryptoKeys/judge/cryptoKeyVersions/1", "principles": { "security": 0.92, "cost": 0.88 }, "constitutionRef": "sha256:abc...", "deliverableHash": "sha256:def...", "issuedAt": "2026-05-16T12:04:30Z", "verdictHash": "BASE64_ED25519_SIGNATURE" }, "spec": { "rootIntent": "...", "componentIntents": [], "taskIntents": [] }}The spec block contains the resolved JSON spec only — never source code, never tool-call outputs. To retrieve worker transcripts, use the editor-local .thoth/cycles/<id>/transcript.jsonl.
API keys
Section titled “API keys”GET /api/v1/auth/api-keys
Section titled “GET /api/v1/auth/api-keys”List API keys for the authenticated tenant. Returns key prefixes only — the secret material is never retrievable after creation.
Response:
{ "keys": [ { "id": "key_01HXXXX", "prefix": "sk_live_a1b2", "name": "production-ci", "scopes": ["cycles:read", "cycles:write"], "createdAt": "2026-05-01T00:00:00Z", "lastUsedAt": "2026-05-16T11:59:00Z", "expiresAt": null } ]}POST /api/v1/auth/api-keys
Section titled “POST /api/v1/auth/api-keys”Create a new API key. The full secret is returned once in the response. Store it immediately.
Request body:
{ "name": "ci-production", "scopes": ["cycles:read", "cycles:write"], "expiresAt": "2027-05-16T00:00:00Z"}| Scope | Permits |
|---|---|
cycles:read | List and inspect cycles. |
cycles:write | Start new cycles. |
keys:read | List API keys (the key’s own prefix only unless admin). |
keys:write | Create and revoke keys. |
admin | Tenant-wide admin. Grant sparingly. |
Response — 201 Created:
{ "id": "key_01HXXXX", "name": "ci-production", "secret": "sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx", "createdAt": "2026-05-16T12:00:00Z", "expiresAt": "2027-05-16T00:00:00Z"}DELETE /api/v1/auth/api-keys/{id}
Section titled “DELETE /api/v1/auth/api-keys/{id}”Revoke an API key. Returns 204 No Content. Existing in-flight cycles authenticated by the key continue to completion; new requests using the key get 401.
GET /api/v1/auth/api-keys/{id}/usage
Section titled “GET /api/v1/auth/api-keys/{id}/usage”Usage metrics for a single key, for the current billing period or a specified period query param (YYYY-MM).
Response:
{ "id": "key_01HXXXX", "period": "2026-05", "remoteCycles": 7, "localCycles": 142, "tokensIn": 1500000, "tokensOut": 380000, "rejectedCycles": 2}localCycles is reported when the plugin is configured with this key and THOTHATO_TELEMETRY=on — it is opt-in usage counting only, not data exfiltration.
Judge public keys
Section titled “Judge public keys”GET /api/v1/judge/public-keys/{kmsKeyVersion}
Section titled “GET /api/v1/judge/public-keys/{kmsKeyVersion}”Fetch the Ed25519 public key for a given Cloud KMS key version. Used to verify signed verdicts. This endpoint is unauthenticated for transparency — anyone can verify a verdict without holding an API key.
Response:
{ "kmsKeyVersion": "projects/p/locations/l/keyRings/r/cryptoKeys/judge/cryptoKeyVersions/1", "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQ...\n-----END PUBLIC KEY-----", "algorithm": "Ed25519", "createdAt": "2026-01-01T00:00:00Z", "rotatedAt": null}See Verdict verification for the full verification protocol and reference implementations in Node, Python, Go, and Rust.
What’s next?
Section titled “What’s next?”- CLI reference — the slash-command surface that uses this API.
- Verdict verification — verify the signed verdicts returned by
/api/v1/cycles/{id}/deliverable. - Pricing — quota and rate-limit tiers.