API Reference

Thermidora API

Everything you need to launch managed AI agents, send messages, and receive real-time events — all via REST.

BASE URLhttps://core.thermidora.com/v1
Authentication

All endpoints (except /auth/* and GET /skills) require authentication. Pass your API key as X-API-Key: tc_live_... or a JWT token as Authorization: Bearer eyJ...

Authentication

All endpoints require authentication via API key or JWT token.

POST/auth/register

Create a new account. An invite code is required during the beta period.

Request Body
{
  "name": "string (required)",
  "email": "string (required)",
  "inviteCode": "string (required)"
}
Response
{
  "accountId": "acc_...",
  "email": "user@example.com",
  "name": "User Name",
  "token": "eyJ..."
}
POST/auth/login

Request a one-time login code sent to your email.

Request Body
{
  "email": "string (required)"
}
Response
{
  "message": "OTP sent to email"
}
POST/auth/verify

Verify the OTP code and receive a JWT token.

Request Body
{
  "email": "string (required)",
  "code": "string (required, 6-digit OTP)"
}
Response
{
  "accountId": "acc_...",
  "token": "eyJ..."
}
GET/auth/me

Get current account info including balance.

Response
{
  "accountId": "acc_...",
  "email": "user@example.com",
  "name": "User Name",
  "balanceUsd": 50.00
}
POST/auth/keys

Create an API key for programmatic access.

Request Body
{
  "name": "string (required)"
}
Response
{
  "keyId": "key_...",
  "apiKey": "tc_live_...",
  "name": "my-key"
}

The apiKey value is only returned once at creation time. Store it securely.

GET/auth/keys

List all API keys for your account.

DELETE/auth/keys/:keyId

Delete an API key.

Instances

An instance is a fully isolated OpenClaw runtime with dedicated compute and storage. Multiple agents can share one instance.

POST/instances

Create a new instance. Provisioning is async — poll GET /instances/:id until status is "running" (~90 seconds).

Request Body
{
  "name": "string (required)"
}
Response
{
  "instanceId": "inst_...",
  "name": "my-agents",
  "status": "provisioning",
  "agents": []
}
GET/instances

List all instances. Status is resolved live from the underlying infrastructure.

GET/instances/:instanceId

Get instance details including live status and agent list.

Response
{
  "instanceId": "inst_...",
  "name": "my-agents",
  "status": "running",
  "agents": [
    { "agentId": "agt_...", "slug": "support-bot", "status": "active" }
  ],
  "startedAt": "2026-04-07T...",
  "totalRuntimeMs": 3600000,
  "billedMs": 3600000
}

Possible status values: provisioning, running, stopped, destroying, error

POST/instances/:instanceId/start

Start a stopped instance. Resumes compute — billing restarts.

Response
{ "instanceId": "inst_...", "status": "running" }
POST/instances/:instanceId/stop

Stop an instance. Compute is deallocated — no charges while stopped. Storage persists.

Response
{ "instanceId": "inst_...", "status": "stopped" }
DELETE/instances/:instanceId

Permanently delete an instance and all its data.

Response
{ "instanceId": "inst_...", "status": "destroying" }

Agents

Agents are AI personas running inside an instance. Each gets its own workspace with persona files, memory, and skills.

POST/instances/:instanceId/agents

Create a new agent. Use useDefaults: true to seed standard workspace files, or provide custom files for full control over persona.

Request Body
{
  "name": "string (required)",
  "slug": "string (required, 2-32 chars, lowercase + hyphens)",
  "displayName": "string (required)",
  "model": "string (optional, default: moonshotai.kimi-k2.5)",
  "useDefaults": true,
  "files": {
    "SOUL.md": "Be helpful and concise...",
    "IDENTITY.md": "Name: Support Bot"
  }
}
Response
{
  "agentId": "agt_...",
  "slug": "support-bot",
  "displayName": "Support Bot",
  "model": "moonshotai.kimi-k2.5",
  "status": "active"
}

Model cannot be changed after creation. Create a new agent to use a different model.

Available models: moonshotai.kimi-k2.5, anthropic.claude-sonnet-4-20250514-v1:0

GET/instances/:instanceId/agents

List all agents in an instance.

GET/instances/:instanceId/agents/:agentId

Get agent details.

PATCH/instances/:instanceId/agents/:agentId

Update agent name, displayName, or workspace files.

Request Body
{
  "displayName": "string (optional)",
  "files": {
    "SOUL.md": "Updated persona..."
  }
}
DELETE/instances/:instanceId/agents/:agentId

Delete an agent and its workspace.

Messages

Send messages to agents and receive replies. Supports both synchronous JSON responses and real-time SSE streaming.

POST/instances/:instanceId/agents/:agentId/messages

Send a message to an agent. Include stream: true for Server-Sent Events streaming.

Request Body
{
  "message": "string (required)",
  "sender": {
    "type": "human",
    "id": "user-123",
    "name": "Jeff"
  },
  "stream": false
}
Response
{
  "messageId": "msg_...",
  "content": "Here's what I found...",
  "sender": {
    "type": "agent",
    "id": "support-bot",
    "name": "Support Bot"
  },
  "timestamp": "2026-04-07T..."
}

When stream: true, the response is an SSE stream of OpenAI-compatible chat completion chunks.

Agents have persistent memory — they remember previous conversations.

GET/instances/:instanceId/agents/:agentId/messages

Get message history for an agent.

Query Parameters
limit   max messages (default 50)

Webhooks

Subscribe to real-time events from your instances. Thermidora delivers events via HTTP POST with HMAC-SHA256 signatures.

POST/webhooks

Create a webhook subscription. Optionally scope to a specific instance or agent.

Request Body
{
  "url": "https://example.com/webhook (required, HTTPS only)",
  "events": ["cron.completed", "agent.message"],
  "instanceId": "inst_... (optional)",
  "description": "string (optional)"
}
Response
{
  "webhookId": "wh_...",
  "url": "https://example.com/webhook",
  "events": ["cron.completed", "agent.message"],
  "secret": "whsec_...",
  "enabled": true
}

Store the secret securely — it is used to verify webhook signatures.

Valid events: cron.completed, agent.message, agent.error, * (wildcard)

GET/webhooks

List all webhook subscriptions. Secrets are partially masked.

GET/webhooks/:webhookId

Get full webhook details including unmasked secret.

PATCH/webhooks/:webhookId

Enable or disable a webhook.

Request Body
{
  "enabled": false
}
DELETE/webhooks/:webhookId

Delete a webhook subscription.

POST/webhooks/:webhookId/test

Send a test event to verify your endpoint is reachable.

Response
{ "message": "Test event dispatched" }

Event Log

Query historical events for an instance — cron job results, agent messages, and errors.

GET/instances/:instanceId/events

List recent events for an instance. Filter by agent or source type.

Query Parameters
limit   max events, default 50, max 200
agent   filter by agent slug
source  filter by source: cron, agent
Response
{
  "events": [
    {
      "messageId": "msg_...",
      "content": "Bitcoin price: $69,327",
      "sender": { "type": "agent", "id": "my-agent", "name": "my-agent" },
      "source": "cron",
      "createdAt": "2026-04-07T09:00:00.000Z",
      "agentSlug": "my-agent"
    }
  ],
  "total": 1
}

Skills

Skills extend what agents can do — weather, web search, code execution, and more. Install built-in skills or upload custom ones.

GET/skills

List all available built-in skills. No authentication required.

Response
[
  {
    "id": "weather",
    "name": "Weather",
    "description": "Get current weather and forecasts",
    "type": "built-in"
  }
]
POST/instances/:instanceId/skills

Install a built-in skill or upload a custom skill with files.

Request Body
// Built-in:
{ "name": "weather", "type": "built-in" }

// Custom:
{
  "name": "my-skill",
  "description": "Custom skill",
  "files": {
    "SKILL.md": "# My Skill\n...",
    "scripts/run.py": "#!/usr/bin/env python3\n..."
  }
}

Custom skills must include a SKILL.md file. All agents in the instance share installed skills.

GET/instances/:instanceId/skills

List skills installed on an instance.

DELETE/instances/:instanceId/skills/:name

Uninstall a skill.

Secrets

Store integration secrets (API keys, tokens) securely. Secrets are injected as environment variables into the instance runtime.

GET/instances/:instanceId/secrets

List secret keys. Values are never exposed.

Response
{
  "secrets": [
    { "key": "OPENAI_API_KEY" },
    { "key": "SLACK_TOKEN" }
  ]
}
POST/instances/:instanceId/secrets

Store one or more secrets. Triggers an instance restart to inject them.

Request Body
{
  "secrets": {
    "OPENAI_API_KEY": "sk-...",
    "SLACK_TOKEN": "xoxb-..."
  }
}
Response
{ "stored": 2, "restarting": true }
DELETE/instances/:instanceId/secrets/:key

Delete a secret.

Webhook Delivery

Events are delivered as HTTP POST requests to your registered URL with HMAC-SHA256 signatures for verification.

Delivery Headers
X-Thermidora-SignatureHMAC-SHA256 hash of the request body
X-Thermidora-EventEvent type (e.g. cron.completed)
X-Thermidora-Webhook-IdYour webhook subscription ID
Example Payload
{
  "eventType": "cron.completed",
  "instanceId": "inst_a1b2c3d4e5f6",
  "agentSlug": "my-agent",
  "data": {
    "content": "Bitcoin price: $69,327 USD",
    "jobName": "bitcoin-price-check"
  },
  "timestamp": "2026-04-07T09:00:00.000Z"
}
Signature Verification (Node.js)
const crypto = require('crypto');

function verifySignature(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(body))
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Usage:
const sig = req.headers['x-thermidora-signature'];
const valid = verifySignature(req.body, sig, webhookSecret);

Errors

All errors follow a consistent format:

{
  "error": {
    "code": "not_found",
    "message": "Instance not found",
    "status": 404
  }
}
400invalid_requestMissing or invalid parameters
401unauthorizedMissing or invalid auth token
404not_foundResource not found or not owned by you
409conflictDuplicate resource (e.g. agent slug exists)
503instance_not_readyInstance is not running
500internal_errorUnexpected server error

Rate Limits

During beta, rate limits are generous. If you hit limits, you'll receive a 429 Too Many Requests response with a Retry-After header.

Ready to build?

Get your API key and launch your first agent in under a minute.