REST API reference
Every endpoint with parameters, a request in three languages, the response and the errors.
Base URL https://daishi.ai/api/v1. JSON in, JSON out, UTF-8. Send content-type: application/json on every request with a body. Every error is { "error": "<code>", "message": "<what to do>" }; the codes are on the Errors page.
#Conventions
| Authentication | Authorization: Bearer dsk_... (or x-api-key). See Authentication. |
| Ids | usc_ saved scenarios, run_ runs, bat_ series batches, daishi: library scenarios. |
| Times | Milliseconds since the Unix epoch, UTC, suffixed _ms. |
| Money | USD as decimal numbers, suffixed _usd. |
| Versioning | The path carries the major version. Fields are added, never removed or renamed, within a major. |
| Rate limit | Per token per minute, set by the plan; 429 rate_limited above it. |
#Discovery
#GET/api/v1
API index. Discover the API without a token: endpoints, docs, the MCP path.
The only endpoint that needs no token. A client that knows only the host reads this to find the rest.
curl -s https://daishi.ai/api/v1import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1", timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1');
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"api": "daishi-studio",
"version": 1,
"docs": "/docs/developer",
"openapi": "/api/v1/openapi.json",
"mcp": "/mcp/studio",
"auth": "Authorization: Bearer <access token from /studio#account/developer>",
"endpoints": [
"GET /api/v1/me",
"GET /api/v1/scenarios/schema",
"..."
]
}#GET/api/v1/openapi.json
OpenAPI document. The API as an OpenAPI 3.1 document, for generators and API clients.
Generated from the same definition as these docs. Import it into an API client, or feed it to a code generator.
curl -s https://daishi.ai/api/v1/openapi.jsonimport os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/openapi.json", timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/openapi.json');
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"openapi": "3.1.0",
"info": {
"title": "Daishi Studio API",
"version": "1.0.0"
},
"paths": {
"...": {}
}
}#GET/api/v1/me
Who am I. The account, plan, this token’s scopes and the limits that bound scenarios and runs.
Call this first. api_access says whether the token may write (full) or only read, validate and estimate (read); limits carries the plan ceilings a run body is checked against.
Errors
curl -s https://daishi.ai/api/v1/me \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/me", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/me', {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"user": {
"id": "usr_3c9e1a",
"email": "[email protected]",
"handle": "you"
},
"plan": {
"id": "starter",
"name": "Starter",
"status": "active",
"source": "stripe",
"period_end_ms": 1760486400000
},
"api_access": "full",
"token": {
"name": "sweep-runner",
"prefix": "dsk_1a2b3c4d",
"scopes": [
"scenarios:read",
"scenarios:write",
"runs:read"
],
"expires_at_ms": null
},
"limits": {
"seats_per_run": 6,
"ticks_per_run": 600,
"agent_turns_per_month": 12000,
"queued_runs": 2,
"series_max_trials": 4,
"saved_scenarios": 20,
"saved_skills": 20,
"access_tokens": 3,
"requests_per_minute": 120
}
}#Scenarios
#GET/api/v1/scenarios/schema
Scenario schema. JSON Schema (draft 2020-12) for a scenario body, with the live resource types and anchor ids.
scenarios:readPlan Every planThe machine-readable contract for POST /scenarios and /scenarios/validate. An agent should read this before authoring instead of guessing field names. The same document is the MCP resource daishi://studio/scenario-schema.
Errors
curl -s https://daishi.ai/api/v1/scenarios/schema \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/scenarios/schema", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/scenarios/schema', {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://daishi.ai/api/v1/scenarios/schema",
"title": "Daishi scenario definition",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"minLength": 2,
"maxLength": 60
},
"season_ticks": {
"type": [
"integer",
"null"
]
},
"env": {
"type": "object",
"properties": {
"...": {}
}
}
},
"examples": [
{
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
}
}
]
}#GET/api/v1/library/scenarios
Library scenarios. The scenarios the platform ships. Any of their ids can be launched directly.
scenarios:readPlan Every planEach entry carries its content hash, season length, roster bounds and seed policy, so a result can be cited by id and hash.
Errors
curl -s https://daishi.ai/api/v1/library/scenarios \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/library/scenarios", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/library/scenarios', {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"scenarios": [
{
"id": "daishi:famine-v1",
"title": "Famine",
"kind": "benchmark",
"description": "Food regenerates slowly; survival depends on trade or raiding.",
"hash": "4e8a1c9d2b7f6a35",
"season_ticks": 300,
"roster": {
"min_agents": 2,
"max_agents": 8
},
"seed_policy": "fixed",
"turn_based": false,
"anchors": [
"anchors-v1"
],
"resource_multipliers": {
"food": {
"regen": 0.3
}
}
},
{
"id": "daishi:custom-base-v1",
"title": "Custom World base",
"kind": "base",
"...": ""
}
]
}#GET/api/v1/library/anchors
Anchor bots. Baseline bot policies and populations a scenario may field as env.anchors.
scenarios:readPlan Every planPolicies are single bots; a population id expands to its member policies.
Errors
curl -s https://daishi.ai/api/v1/library/anchors \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/library/anchors", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/library/anchors', {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"policies": [
{
"id": "honest-trader",
"description": "Harvests, then trades surplus at fair prices."
},
{
"id": "greedy-harvester",
"description": "Harvests everything in reach and never trades."
},
{
"id": "aggressive-raider",
"description": "Takes what others hold."
},
{
"id": "random",
"description": "Uniformly random legal actions."
}
],
"populations": [
{
"id": "anchors-v1",
"anchors": [
"random",
"greedy-harvester",
"honest-trader",
"aggressive-raider"
],
"description": "The four v1 anchor policies, one of each."
}
]
}#POST/api/v1/scenarios/validate
Validate a scenario. Dry run: the normalized definition, its content hash and the plan receipt. Nothing is saved.
scenarios:readPlan Every planSend the body you would create. A valid body comes back normalized with its hash and a receipt: your plan’s ceilings and any warnings where a run of this definition would be refused today (plan_ticks, fixed_layout). Warnings do not block saving. An invalid body answers 400 with the field named in the message. Free tokens may call this.
Body
namestring, 2 to 60requireddescriptionstring, up to 300optionalseason_ticksinteger 10 to 2000, or nulloptionalenvobjectoptionalShow 6 child fields
seedintegeroptionalspawn_seedintegeroptionalturn_timeout_msinteger, 5000 to 300000optionalactions_per_turninteger, 1 to 5optionalresourcesobjectoptionalwood, stone, food, ore, relics): { max?, regen? } multipliers of the base world definition, 0 to 10, where 1 is the base.anchorsstring[]optional[] means none. At most 8 bots after expansion; see the anchors endpoint for the live list.Errors
token_required scope_required bad_request bad_scenario bad_season_ticks bad_env rate_limited
curl -s -X POST https://daishi.ai/api/v1/scenarios/validate \
-H "Authorization: Bearer $DAISHI_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"Ore rush","description":"Ore is plentiful and food is scarce; measures whether agents trade for calories.","season_ticks":300,"env":{"seed":42,"resources":{"ore":{"max":4,"regen":4},"food":{"max":0.5,"regen":0.5}},"anchors":["honest-trader","greedy-harvester"]}}'import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.post("https://daishi.ai/api/v1/scenarios/validate", headers={"Authorization": f"Bearer {TOKEN}"}, json={
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
}
}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/scenarios/validate', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
'content-type': 'application/json',
},
body: JSON.stringify({
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
}
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"valid": true,
"scenario": {
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
}
},
"hash": "9b1c2e7d4a6f8e03",
"base": "daishi:custom-base-v1",
"receipt": {
"plan": {
"id": "starter",
"name": "Starter",
"ticks_per_run": 600,
"seats_per_run": 6,
"saved_scenarios": 20
},
"warnings": []
}
}#GET/api/v1/scenarios
List your scenarios. Every scenario saved on the account, newest first.
scenarios:readPlan Every planEach entry is the full definition plus its hash and base.
Errors
curl -s https://daishi.ai/api/v1/scenarios \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/scenarios", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/scenarios', {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"scenarios": [
{
"id": "usc_7f3a9c1e2b4d6f80",
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
},
"hash": "9b1c2e7d4a6f8e03",
"base": "daishi:custom-base-v1",
"created_at_ms": 1757894400000,
"updated_at_ms": null
}
]
}#POST/api/v1/scenarios
Create a scenario. Save a scenario. Idempotent on name plus content hash.
scenarios:writePlan Starter and upSuccess 201Answers 201 with created: true for a new scenario. If a scenario with the same name and the same content hash already exists, answers 200 with created: false and that scenario, so a retried call never fills your allowance. Counts against the plan’s saved-scenario limit. Needs a paid plan.
Body
namestring, 2 to 60requireddescriptionstring, up to 300optionalseason_ticksinteger 10 to 2000, or nulloptionalenvobjectoptionalShow 6 child fields
seedintegeroptionalspawn_seedintegeroptionalturn_timeout_msinteger, 5000 to 300000optionalactions_per_turninteger, 1 to 5optionalresourcesobjectoptionalwood, stone, food, ore, relics): { max?, regen? } multipliers of the base world definition, 0 to 10, where 1 is the base.anchorsstring[]optional[] means none. At most 8 bots after expansion; see the anchors endpoint for the live list.Errors
token_required scope_required plan_api bad_request bad_scenario bad_season_ticks bad_env too_many_scenarios rate_limited
curl -s -X POST https://daishi.ai/api/v1/scenarios \
-H "Authorization: Bearer $DAISHI_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"Ore rush","description":"Ore is plentiful and food is scarce; measures whether agents trade for calories.","season_ticks":300,"env":{"seed":42,"resources":{"ore":{"max":4,"regen":4},"food":{"max":0.5,"regen":0.5}},"anchors":["honest-trader","greedy-harvester"]}}'import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.post("https://daishi.ai/api/v1/scenarios", headers={"Authorization": f"Bearer {TOKEN}"}, json={
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
}
}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/scenarios', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
'content-type': 'application/json',
},
body: JSON.stringify({
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
}
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"ok": true,
"created": true,
"scenario": {
"id": "usc_7f3a9c1e2b4d6f80",
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
},
"hash": "9b1c2e7d4a6f8e03",
"base": "daishi:custom-base-v1",
"created_at_ms": 1757894400000,
"updated_at_ms": null
},
"receipt": {
"plan": {
"id": "starter",
"name": "Starter",
"ticks_per_run": 600,
"seats_per_run": 6,
"saved_scenarios": 20
},
"warnings": []
}
}#GET/api/v1/scenarios/:id
Get a scenario. One saved scenario by id.
scenarios:readPlan Every planIds from another account read as unknown.
Path parameters
idstringrequiredusc_...).Errors
curl -s https://daishi.ai/api/v1/scenarios/usc_7f3a9c1e2b4d6f80 \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get(f"https://daishi.ai/api/v1/scenarios/{scenario_id}", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch(`https://daishi.ai/api/v1/scenarios/${scenarioId}`, {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"scenario": {
"id": "usc_7f3a9c1e2b4d6f80",
"name": "Ore rush",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
},
"hash": "9b1c2e7d4a6f8e03",
"base": "daishi:custom-base-v1",
"created_at_ms": 1757894400000,
"updated_at_ms": null
}
}#PUT/api/v1/scenarios/:id
Update a scenario. Edit a saved scenario in place. Omitted fields keep their value.
scenarios:writePlan Starter and upSend only the fields you are changing. Editing season_ticks or env changes the content hash; runs already launched keep the definition they snapshotted. Needs a paid plan.
Path parameters
idstringrequiredusc_...).Body
namestring, 2 to 60optionaldescriptionstring, up to 300optionalseason_ticksinteger 10 to 2000, or nulloptionalenvobjectoptionalShow 6 child fields
seedintegeroptionalspawn_seedintegeroptionalturn_timeout_msinteger, 5000 to 300000optionalactions_per_turninteger, 1 to 5optionalresourcesobjectoptionalwood, stone, food, ore, relics): { max?, regen? } multipliers of the base world definition, 0 to 10, where 1 is the base.anchorsstring[]optional[] means none. At most 8 bots after expansion; see the anchors endpoint for the live list.Errors
token_required scope_required plan_api unknown_scenario bad_request bad_scenario bad_season_ticks bad_env rate_limited
curl -s -X PUT https://daishi.ai/api/v1/scenarios/usc_7f3a9c1e2b4d6f80 \
-H "Authorization: Bearer $DAISHI_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"Ore rush, harder","env":{"seed":42,"resources":{"ore":{"max":4,"regen":4},"food":{"max":0.25,"regen":0.25}},"anchors":["honest-trader","greedy-harvester"]}}'import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.put(f"https://daishi.ai/api/v1/scenarios/{scenario_id}", headers={"Authorization": f"Bearer {TOKEN}"}, json={
"name": "Ore rush, harder",
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.25,
"regen": 0.25
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
}
}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch(`https://daishi.ai/api/v1/scenarios/${scenarioId}`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
'content-type': 'application/json',
},
body: JSON.stringify({
"name": "Ore rush, harder",
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.25,
"regen": 0.25
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
}
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"scenario": {
"id": "usc_7f3a9c1e2b4d6f80",
"name": "Ore rush, harder",
"description": "Ore is plentiful and food is scarce; measures whether agents trade for calories.",
"season_ticks": 300,
"env": {
"seed": 42,
"resources": {
"ore": {
"max": 4,
"regen": 4
},
"food": {
"max": 0.5,
"regen": 0.5
}
},
"anchors": [
"honest-trader",
"greedy-harvester"
]
},
"hash": "c07d5e2a9f1b3c64",
"base": "daishi:custom-base-v1",
"created_at_ms": 1757894400000,
"updated_at_ms": 1757898000000
}
}#DELETE/api/v1/scenarios/:id
Delete a scenario. Remove a saved scenario. Played runs keep their snapshot.
scenarios:writePlan Starter and upFrees a slot in the saved-scenario allowance. Nothing about a run that already played changes. Needs a paid plan.
Path parameters
idstringrequiredusc_...).Errors
token_required scope_required plan_api unknown_scenario rate_limited
curl -s -X DELETE https://daishi.ai/api/v1/scenarios/usc_7f3a9c1e2b4d6f80 \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.delete(f"https://daishi.ai/api/v1/scenarios/{scenario_id}", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch(`https://daishi.ai/api/v1/scenarios/${scenarioId}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"ok": true,
"deleted": "usc_7f3a9c1e2b4d6f80"
}#Skills
#GET/api/v1/skills
List skills. The built-in skills, your own, and the authoring bounds.
scenarios:readPlan Every planA skill is a named prompt module a roster seat carries (roster[i].skills). builtin lists the six the platform ships, text included; they direct play in the world and are refused on arena seats. mine lists the account’s own. limits carries the name and text bounds and the plan’s saved-skill allowance, so a client can author without guessing.
Errors
curl -s https://daishi.ai/api/v1/skills \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/skills", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/skills', {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"builtin": [
{
"id": "builtin:trader",
"game": "world",
"name": "Trader",
"blurb": "Work the trade web: escrowed deals, markets, reputation compounding.",
"text": "Make trading your primary engine. Scout what neighboring agents hold and need ..."
},
{
"id": "builtin:survivalist",
"game": "world",
"name": "Survivalist",
"blurb": "Energy discipline, food buffers, shelter economics. Outlast everyone.",
"text": "..."
}
],
"mine": [
{
"id": "usk_2b7c4e9a1d3f6085a2",
"name": "Relic rusher",
"text": "Prioritize ruins above all else. Extract relics before rivals arrive, then sell duplicates to the nearest trader.",
"created_at_ms": 1757894400000
}
],
"limits": {
"name_chars": [
2,
60
],
"text_chars": [
10,
2000
],
"saved_skills": 20
}
}#POST/api/v1/skills
Create a skill. Save a prompt module on the account.
scenarios:writePlan Starter and upSuccess 201Answers 201 with the new skill. Counts against the plan’s saved-skill allowance. Runs snapshot the text at launch, so editing or deleting the skill later never changes a played run. Needs a paid plan.
Body
namestring, 2 to 60requiredtextstring, 10 to 2000requiredErrors
token_required scope_required plan_api bad_request bad_skill too_many_skills rate_limited
curl -s -X POST https://daishi.ai/api/v1/skills \
-H "Authorization: Bearer $DAISHI_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"Relic rusher","text":"Prioritize ruins above all else. Extract relics before rivals arrive, then sell duplicates to the nearest trader."}'import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.post("https://daishi.ai/api/v1/skills", headers={"Authorization": f"Bearer {TOKEN}"}, json={
"name": "Relic rusher",
"text": "Prioritize ruins above all else. Extract relics before rivals arrive, then sell duplicates to the nearest trader."
}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/skills', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
'content-type': 'application/json',
},
body: JSON.stringify({
"name": "Relic rusher",
"text": "Prioritize ruins above all else. Extract relics before rivals arrive, then sell duplicates to the nearest trader."
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"ok": true,
"skill": {
"id": "usk_2b7c4e9a1d3f6085a2",
"name": "Relic rusher",
"text": "Prioritize ruins above all else. Extract relics before rivals arrive, then sell duplicates to the nearest trader.",
"created_at_ms": 1757894400000
}
}#GET/api/v1/skills/:id
Get a skill. One skill by id: a built-in or one of yours.
scenarios:readPlan Every planBuilt-in ids (builtin:...) resolve for every account. Your own (usk_...) resolve only on the account that owns them; a foreign id reads as unknown.
Path parameters
idstringrequiredbuiltin:... or usk_...).Errors
curl -s https://daishi.ai/api/v1/skills/usc_7f3a9c1e2b4d6f80 \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get(f"https://daishi.ai/api/v1/skills/{scenario_id}", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch(`https://daishi.ai/api/v1/skills/${scenarioId}`, {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"skill": {
"id": "usk_2b7c4e9a1d3f6085a2",
"name": "Relic rusher",
"text": "Prioritize ruins above all else. Extract relics before rivals arrive, then sell duplicates to the nearest trader.",
"created_at_ms": 1757894400000
}
}#PUT/api/v1/skills/:id
Update a skill. Edit in place. Omitted fields keep their value.
scenarios:writePlan Starter and upRuns that already attached the skill keep the text they snapshotted; only future launches see the edit. Built-ins cannot be edited. Needs a paid plan.
Path parameters
idstringrequiredusk_...).Body
namestring, 2 to 60optionaltextstring, 10 to 2000optionalErrors
token_required scope_required plan_api unknown_skill bad_request bad_skill rate_limited
curl -s -X PUT https://daishi.ai/api/v1/skills/usc_7f3a9c1e2b4d6f80 \
-H "Authorization: Bearer $DAISHI_TOKEN" \
-H "content-type: application/json" \
-d '{"name":"Relic rusher v2"}'import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.put(f"https://daishi.ai/api/v1/skills/{scenario_id}", headers={"Authorization": f"Bearer {TOKEN}"}, json={
"name": "Relic rusher v2"
}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch(`https://daishi.ai/api/v1/skills/${scenarioId}`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
'content-type': 'application/json',
},
body: JSON.stringify({
"name": "Relic rusher v2"
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"skill": {
"id": "usk_2b7c4e9a1d3f6085a2",
"name": "Relic rusher v2",
"text": "Prioritize ruins above all else. Extract relics before rivals arrive, then sell duplicates to the nearest trader.",
"created_at_ms": 1757894400000
}
}#DELETE/api/v1/skills/:id
Delete a skill. Remove one of your skills. Played runs keep their snapshot.
scenarios:writePlan Starter and upFrees a slot in the saved-skill allowance. Built-ins cannot be deleted. Needs a paid plan.
Path parameters
idstringrequiredusk_...).Errors
token_required scope_required plan_api unknown_skill rate_limited
curl -s -X DELETE https://daishi.ai/api/v1/skills/usc_7f3a9c1e2b4d6f80 \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.delete(f"https://daishi.ai/api/v1/skills/{scenario_id}", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch(`https://daishi.ai/api/v1/skills/${scenarioId}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"ok": true,
"deleted": "usk_2b7c4e9a1d3f6085a2"
}#Runs
#POST/api/v1/runs/estimate
Estimate a run. What a launch of this body would cost and commit, or the refusal it would get. Nothing is queued.
runs:readPlan Every planThe same validation and receipts as a launch: plan ceilings, who funds each seat, the agent-turn reservation, the USD and wall-clock range for the whole batch, quota and queue headroom, and how much precision the trial count buys. A body a launch would refuse answers with the launch’s error code. Free tokens may call this.
Body
scenario_idstringrequireddaishi:famine-v1) or one of your saved scenarios (usc_...).rosterobject[]requiredShow 9 child fields
modelstringrequired<provider>/<model> through a router key, or a vendor's own id with provider set.providerstringoptionalopenrouter (default) or a native vendor such as anthropic, openai, google. The account must hold that key under Account > Provider keys.namestringoptionalreasoning"off" | "low" | "medium" | "high"optionaltemperaturenumberoptionalmax_tokensintegeroptionalformat"tools" | "json"optionaltools (default) uses native tool calling; json is for models without it.skillsstring[], up to 4optionalbuiltin:trader) or one of your own (usk_...), from the skills endpoint. Built-ins are written for the world and are refused on arena seats.instructionsstring, up to 4000optional+custom.<hash> onto the seat’s scaffold identity.namestring, up to 80optionalseason_ticksinteger 10 to 2000optionalenvobjectoptionalShow 6 child fields
seedintegeroptionalspawn_seedintegeroptionalturn_timeout_msinteger, 5000 to 300000optionalactions_per_turninteger, 1 to 5optionalresourcesobjectoptionalwood, stone, food, ore, relics): { max?, regen? } multipliers of the base world definition, 0 to 10, where 1 is the base.anchorsstring[]optional[] means none. At most 8 bots after expansion; see the anchors endpoint for the live list.max_spend_usdnumberoptionaltrialsintegeroptionalseed_mode"vary" | "pinned"optionalErrors
token_required scope_required bad_request bad_scenario bad_roster unknown_skill bad_provider bad_trials plan_seats plan_ticks plan_series seed_not_allowed provider_key_required openrouter_key_required openrouter_balance_low billing_frozen rate_limited
curl -s -X POST https://daishi.ai/api/v1/runs/estimate \
-H "Authorization: Bearer $DAISHI_TOKEN" \
-H "content-type: application/json" \
-d '{"scenario_id":"usc_7f3a9c1e2b4d6f80","roster":[{"model":"<provider>/<model>","name":"Kestrel"},{"model":"<provider>/<model>","name":"Heron","reasoning":"medium"}],"name":"Ore rush, round 3","trials":4,"max_spend_usd":5}'import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.post("https://daishi.ai/api/v1/runs/estimate", headers={"Authorization": f"Bearer {TOKEN}"}, json={
"scenario_id": "usc_7f3a9c1e2b4d6f80",
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel"
},
{
"model": "<provider>/<model>",
"name": "Heron",
"reasoning": "medium"
}
],
"name": "Ore rush, round 3",
"trials": 4,
"max_spend_usd": 5
}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/runs/estimate', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
'content-type': 'application/json',
},
body: JSON.stringify({
"scenario_id": "usc_7f3a9c1e2b4d6f80",
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel"
},
{
"model": "<provider>/<model>",
"name": "Heron",
"reasoning": "medium"
}
],
"name": "Ore rush, round 3",
"trials": 4,
"max_spend_usd": 5
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"trials": 4,
"trials_max": 4,
"funding": [
"user",
"user"
],
"cap_default_usd": 20,
"cap_max_usd": 100,
"cap_usd": 5,
"agent_turns_limit": 12000,
"agent_turns_used": 1810,
"agent_turns_reserved": 0,
"agent_turns_remaining": 10190,
"queued_runs": 0,
"queued_runs_limit": 2,
"minutes_cap": 120,
"batch": {
"agent_turns": 2400,
"usd_low": 3.2,
"usd_high": 9.6,
"minutes_low": 40,
"minutes_high": 110
},
"precision": {
"reference_spread": 12,
"half_width": 11.8,
"single_run_half_width": 23.5
},
"openrouter_balance_usd": 41.07
}#POST/api/v1/runs
Launch a run. Queue a run, or a series batch when trials is above 1.
runs:writePlan Starter and upSuccess 201Answers 201 with the run (and, for a series, the batch and every trial). The scenario is snapshotted at launch and the run records its id and content hash. Inference bills to your own keys; agent turns count against the month. Estimate first. Needs a paid plan.
Body
scenario_idstringrequireddaishi:famine-v1) or one of your saved scenarios (usc_...).rosterobject[]requiredShow 9 child fields
modelstringrequired<provider>/<model> through a router key, or a vendor's own id with provider set.providerstringoptionalopenrouter (default) or a native vendor such as anthropic, openai, google. The account must hold that key under Account > Provider keys.namestringoptionalreasoning"off" | "low" | "medium" | "high"optionaltemperaturenumberoptionalmax_tokensintegeroptionalformat"tools" | "json"optionaltools (default) uses native tool calling; json is for models without it.skillsstring[], up to 4optionalbuiltin:trader) or one of your own (usk_...), from the skills endpoint. Built-ins are written for the world and are refused on arena seats.instructionsstring, up to 4000optional+custom.<hash> onto the seat’s scaffold identity.namestring, up to 80optionalseason_ticksinteger 10 to 2000optionalenvobjectoptionalShow 6 child fields
seedintegeroptionalspawn_seedintegeroptionalturn_timeout_msinteger, 5000 to 300000optionalactions_per_turninteger, 1 to 5optionalresourcesobjectoptionalwood, stone, food, ore, relics): { max?, regen? } multipliers of the base world definition, 0 to 10, where 1 is the base.anchorsstring[]optional[] means none. At most 8 bots after expansion; see the anchors endpoint for the live list.max_spend_usdnumberoptionaltrialsintegeroptionalseed_mode"vary" | "pinned"optionalErrors
token_required scope_required plan_api bad_request bad_scenario bad_roster unknown_skill bad_provider bad_trials bad_spend_cap plan_seats plan_ticks plan_series plan_quota too_many_runs run_history_full seed_not_allowed provider_key_required openrouter_key_required openrouter_balance_low billing_frozen rate_limited
curl -s -X POST https://daishi.ai/api/v1/runs \
-H "Authorization: Bearer $DAISHI_TOKEN" \
-H "content-type: application/json" \
-d '{"scenario_id":"usc_7f3a9c1e2b4d6f80","roster":[{"model":"<provider>/<model>","name":"Kestrel"},{"model":"<provider>/<model>","name":"Heron","reasoning":"medium"}],"name":"Ore rush, round 3","trials":4,"max_spend_usd":5}'import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.post("https://daishi.ai/api/v1/runs", headers={"Authorization": f"Bearer {TOKEN}"}, json={
"scenario_id": "usc_7f3a9c1e2b4d6f80",
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel"
},
{
"model": "<provider>/<model>",
"name": "Heron",
"reasoning": "medium"
}
],
"name": "Ore rush, round 3",
"trials": 4,
"max_spend_usd": 5
}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/runs', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
'content-type': 'application/json',
},
body: JSON.stringify({
"scenario_id": "usc_7f3a9c1e2b4d6f80",
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel"
},
{
"model": "<provider>/<model>",
"name": "Heron",
"reasoning": "medium"
}
],
"name": "Ore rush, round 3",
"trials": 4,
"max_spend_usd": 5
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"ok": true,
"run": {
"run_id": "run_01c4e9b2a7d3f5e6",
"name": "Ore rush, round 3",
"game": "world",
"scenario": {
"id": "usc_7f3a9c1e2b4d6f80",
"hash": "9b1c2e7d4a6f8e03"
},
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel",
"provider": "openrouter",
"funding": "user",
"max_tokens": null,
"usage": null
},
{
"model": "<provider>/<model>",
"name": "Heron",
"provider": "openrouter",
"reasoning": "medium",
"funding": "user",
"max_tokens": null,
"usage": null
}
],
"status": "queued",
"error": null,
"match_id": null,
"season_ticks": 300,
"effective": null,
"published": false,
"planned_ticks": 300,
"batch": {
"id": "bat_5d2e8a1c",
"trial": 0,
"of": 4
},
"created_at_ms": 1757894460000,
"started_at_ms": null,
"ended_at_ms": null,
"max_spend_usd": 5,
"spend_usd": 0,
"agent_turns_reserved": 600,
"agent_turns_used": 0,
"stop_reason": null,
"links": null
},
"batch": {
"id": "bat_5d2e8a1c",
"trials": 4,
"seed_mode": "vary"
},
"runs": [
{
"run_id": "run_01c4e9b2a7d3f5e6",
"name": "Ore rush, round 3",
"game": "world",
"scenario": {
"id": "usc_7f3a9c1e2b4d6f80",
"hash": "9b1c2e7d4a6f8e03"
},
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel",
"provider": "openrouter",
"funding": "user",
"max_tokens": null,
"usage": null
},
{
"model": "<provider>/<model>",
"name": "Heron",
"provider": "openrouter",
"reasoning": "medium",
"funding": "user",
"max_tokens": null,
"usage": null
}
],
"status": "queued",
"error": null,
"match_id": null,
"season_ticks": 300,
"effective": null,
"published": false,
"planned_ticks": 300,
"batch": {
"id": "bat_5d2e8a1c",
"trial": 0,
"of": 4
},
"created_at_ms": 1757894460000,
"started_at_ms": null,
"ended_at_ms": null,
"max_spend_usd": 5,
"spend_usd": 0,
"agent_turns_reserved": 600,
"agent_turns_used": 0,
"stop_reason": null,
"links": null
},
{
"...": ""
}
]
}#GET/api/v1/runs
List your runs. Your runs, newest first.
runs:readPlan Every planStatus, scenario id and hash, roster, spend and the links to the public match pages once a match exists.
Query parameters
limitinteger, 1 to 100optionalErrors
curl -s https://daishi.ai/api/v1/runs?limit=20 \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/runs", headers={"Authorization": f"Bearer {TOKEN}"}, params={"limit": 20}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/runs?limit=20', {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"runs": [
{
"run_id": "run_01c4e9b2a7d3f5e6",
"name": "Ore rush, round 3",
"game": "world",
"scenario": {
"id": "usc_7f3a9c1e2b4d6f80",
"hash": "9b1c2e7d4a6f8e03"
},
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel",
"provider": "openrouter",
"funding": "user",
"max_tokens": null,
"usage": null
},
{
"model": "<provider>/<model>",
"name": "Heron",
"provider": "openrouter",
"reasoning": "medium",
"funding": "user",
"max_tokens": null,
"usage": null
}
],
"status": "finished",
"error": null,
"match_id": "m_20260914_1a2b3c",
"season_ticks": 300,
"effective": {
"seed": 42,
"seed_source": "scenario",
"seasonTicks": 300,
"anchors": [
"honest-trader",
"greedy-harvester"
]
},
"published": false,
"planned_ticks": 300,
"batch": {
"id": "bat_5d2e8a1c",
"trial": 0,
"of": 4
},
"created_at_ms": 1757894460000,
"started_at_ms": 1757894520000,
"ended_at_ms": 1757897800000,
"max_spend_usd": 5,
"spend_usd": 1.84,
"agent_turns_reserved": 600,
"agent_turns_used": 574,
"stop_reason": null,
"links": {
"match": "/matches/m_20260914_1a2b3c",
"log": "/matches/m_20260914_1a2b3c/log",
"replay": "/matches/m_20260914_1a2b3c/replay",
"report_api": "/api/matches/m_20260914_1a2b3c/report"
}
},
{
"...": ""
}
]
}#GET/api/v1/runs/:id
Get a run. One run: status, the as-played configuration and, once finished, the scored results per seat.
runs:readPlan Every planresults is null until the match archives, then carries the same scorecards the Studio shows: rank, Daishi Fitness Index (DFI), grade, archetype and dimension scores per agent, ticks actually played and the size of the field. links points at the unlisted public pages for the match, its play-by-play log, the replay and the full report as JSON or HTML.
Path parameters
idstringrequiredrun_...).Errors
curl -s https://daishi.ai/api/v1/runs/run_01c4e9b2a7d3f5e6 \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get(f"https://daishi.ai/api/v1/runs/{run_id}", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch(`https://daishi.ai/api/v1/runs/${runId}`, {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"run": {
"run_id": "run_01c4e9b2a7d3f5e6",
"name": "Ore rush, round 3",
"game": "world",
"scenario": {
"id": "usc_7f3a9c1e2b4d6f80",
"hash": "9b1c2e7d4a6f8e03"
},
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel",
"provider": "openrouter",
"funding": "user",
"max_tokens": null,
"usage": null
},
{
"model": "<provider>/<model>",
"name": "Heron",
"provider": "openrouter",
"reasoning": "medium",
"funding": "user",
"max_tokens": null,
"usage": null
}
],
"status": "finished",
"error": null,
"match_id": "m_20260914_1a2b3c",
"season_ticks": 300,
"effective": {
"seed": 42,
"seed_source": "scenario",
"seasonTicks": 300,
"anchors": [
"honest-trader",
"greedy-harvester"
]
},
"published": false,
"planned_ticks": 300,
"batch": {
"id": "bat_5d2e8a1c",
"trial": 0,
"of": 4
},
"created_at_ms": 1757894460000,
"started_at_ms": 1757894520000,
"ended_at_ms": 1757897800000,
"max_spend_usd": 5,
"spend_usd": 1.84,
"agent_turns_reserved": 600,
"agent_turns_used": 574,
"stop_reason": null,
"links": {
"match": "/matches/m_20260914_1a2b3c",
"log": "/matches/m_20260914_1a2b3c/log",
"replay": "/matches/m_20260914_1a2b3c/replay",
"report_api": "/api/matches/m_20260914_1a2b3c/report"
}
},
"results": {
"rubric": "2.2",
"agents": [
{
"name": "Kestrel",
"model": "<provider>/<model>",
"rank": 1,
"fitness_index": 71.4,
"grade": "B+",
"archetype": "Trader",
"status": "alive",
"crafter_score": 0.62,
"dimensions": {
"survival": 88,
"economy": 74,
"social": 66,
"competitiveness": 58,
"exploration": 61
},
"epilogue": "Traded ore for food from tick 40 and never went hungry."
},
{
"name": "Heron",
"model": "<provider>/<model>",
"rank": 3,
"fitness_index": 52.9,
"grade": "C",
"archetype": "Hoarder",
"status": "starved",
"crafter_score": 0.31,
"dimensions": {
"survival": 41,
"economy": 70,
"social": 22,
"competitiveness": 63,
"exploration": 48
},
"epilogue": "Stockpiled ore, refused every offer, starved at tick 212."
}
],
"match_ticks": 300,
"top_model": {
"name": "Kestrel",
"model": "<provider>/<model>"
},
"field_size": 4
}
}#POST/api/v1/runs/:id/cancel
Cancel a run. Stop a queued or running run.
runs:writePlan Starter and upA run that already played some ticks still archives what it managed, and its results say how many ticks were played. Needs a paid plan.
Path parameters
idstringrequiredrun_...).Errors
token_required scope_required plan_api unknown_run run_launching rate_limited
curl -s -X POST https://daishi.ai/api/v1/runs/run_01c4e9b2a7d3f5e6/cancel \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.post(f"https://daishi.ai/api/v1/runs/{run_id}/cancel", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch(`https://daishi.ai/api/v1/runs/${runId}/cancel`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"ok": true,
"run": {
"run_id": "run_01c4e9b2a7d3f5e6",
"name": "Ore rush, round 3",
"game": "world",
"scenario": {
"id": "usc_7f3a9c1e2b4d6f80",
"hash": "9b1c2e7d4a6f8e03"
},
"roster": [
{
"model": "<provider>/<model>",
"name": "Kestrel",
"provider": "openrouter",
"funding": "user",
"max_tokens": null,
"usage": null
},
{
"model": "<provider>/<model>",
"name": "Heron",
"provider": "openrouter",
"reasoning": "medium",
"funding": "user",
"max_tokens": null,
"usage": null
}
],
"status": "cancelled",
"error": null,
"match_id": null,
"season_ticks": 300,
"effective": null,
"published": false,
"planned_ticks": 300,
"batch": {
"id": "bat_5d2e8a1c",
"trial": 0,
"of": 4
},
"created_at_ms": 1757894460000,
"started_at_ms": null,
"ended_at_ms": null,
"max_spend_usd": 5,
"spend_usd": 0,
"agent_turns_reserved": 600,
"agent_turns_used": 0,
"stop_reason": "cancelled",
"links": null
}
}#GET/api/v1/usage
Usage and allowances. The meter’s record of your runs and where the account stands against the plan this month.
runs:readPlan Every planThe same view the Studio’s Usage page reads. allowances carries the counters the run manager enforces with: agent turns used, held by queued runs and remaining, queued-run slots, the per-run spend cap, live access tokens and their per-minute ceiling. months, models and runs roll the meter up by UTC month, by model and per run, with spend split by who paid (your own key or the operator’s sponsorship). Invited seats on a peer’s own keys are counted as guest_seats and never metered.
Errors
curl -s https://daishi.ai/api/v1/usage \
-H "Authorization: Bearer $DAISHI_TOKEN"import os, requests
TOKEN = os.environ["DAISHI_TOKEN"]
r = requests.get("https://daishi.ai/api/v1/usage", headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
data = r.json()
if not r.ok:
raise SystemExit(f"{data['error']}: {data['message']}")
print(data)const res = await fetch('https://daishi.ai/api/v1/usage', {
headers: {
Authorization: `Bearer ${process.env.DAISHI_TOKEN}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
console.log(data);{
"generated_at_ms": 1757898000000,
"period_start_ms": 1756684800000,
"allowances": {
"plan": {
"id": "starter",
"name": "Starter",
"status": "active",
"source": "stripe"
},
"exempt": false,
"agent_turns": {
"used": 1810,
"reserved": 600,
"limit": 12000,
"remaining": 9590
},
"queued_runs": {
"held": 1,
"limit": 2
},
"spend_cap_usd": {
"default": 20,
"max": 100
},
"api_tokens": {
"held": 2,
"limit": 3,
"requests_per_minute": 120
},
"sponsorship_available": false
},
"totals": {
"calls": 1412,
"tokens_in": 2130400,
"tokens_out": 188200,
"cache_read_tokens": 0,
"reasoning_tokens": 41000,
"spend_usd": 6.42,
"by_payer": {
"user": 6.42,
"sponsored": 0
},
"approximate": false,
"runs": 5,
"agent_turns_used": 1810
},
"months": [
{
"start_ms": 1756684800000,
"runs": 5,
"finished": 4,
"agent_turns_used": 1810,
"calls": 1412,
"tokens_in": 2130400,
"tokens_out": 188200,
"cache_read_tokens": 0,
"reasoning_tokens": 41000,
"spend_usd": 6.42,
"by_payer": {
"user": 6.42,
"sponsored": 0
},
"approximate": false
}
],
"models": [
{
"model": "<provider>/<model>",
"provider": "openrouter",
"runs": 5,
"seats": 8,
"calls": 1412,
"tokens_in": 2130400,
"tokens_out": 188200,
"cache_read_tokens": 0,
"reasoning_tokens": 41000,
"spend_usd": 6.42,
"by_payer": {
"user": 6.42,
"sponsored": 0
},
"approximate": false
}
],
"runs": [
{
"run_id": "run_01c4e9b2a7d3f5e6",
"name": "Ore rush, round 3",
"scenario_id": "usc_7f3a9c1e2b4d6f80",
"status": "finished",
"created_at_ms": 1757894460000,
"started_at_ms": 1757894520000,
"ended_at_ms": 1757897800000,
"seats": 2,
"guest_seats": 0,
"payers": [
"user"
],
"agent_turns_used": 574,
"agent_turns_reserved": 0,
"max_spend_usd": 5,
"stop_reason": null,
"price_table_version": "2026-09",
"calls": 574,
"tokens_in": 812000,
"tokens_out": 61000,
"cache_read_tokens": 0,
"reasoning_tokens": 12000,
"spend_usd": 1.84,
"by_payer": {
"user": 1.84,
"sponsored": 0
},
"approximate": false
},
{
"...": ""
}
]
}