Module 19
Autonomous Agent API
https://dicesim.com/api/v1.Overview
The agent API exposes four tools, named after the MCP-style tool contract in the downloadable skill file: dicecraft_simulate (POST /agent/sandbox/execute), dicecraft_save_vault (POST /agent/strategies), dicecraft_marketplace_list (POST /agent/marketplace/publish) and the account query GET /agent/account/credits.
Simulations never run on the API process. They are proxied to an isolated sim-service that executes Lua 5.4 in a worker pool with hard caps: 1,000,000 rolls per call, 64 KB of Lua, 30 s wall time.
Every response is an envelope: { "data": … } on success, { "error": { "code", "message", "details?" } } on failure.
Create an API key
- Sign in and open Dashboard → API keys.
- Give the key a label and pick its permission scopes:
simulate,strategies,publish. Grant only what the agent needs. A research agent rarely needspublish. - Copy the secret immediately. It is shown exactly once; afterwards only the prefix (
dc_live_0f3a9c1e…) is visible, and a lost key must be revoked and re-created.
The same operation is available to scripts through the session-authenticated endpoint POST /api/v1/users/api-keys (cookie or JWT, not an API key). Keys are stored hashed; the server cannot recover a secret.
{
"label": "research-agent",
"permissions": [
"simulate",
"strategies"
]
}{
"data": {
"id": "2e1b7d6a-4f0c-4c8e-9a1d-7b3e5f2c8d90",
"keyPrefix": "dc_live_0f3a9c1e",
"label": "research-agent",
"permissions": [
"simulate",
"strategies"
],
"lastUsedAt": null,
"createdAt": "2026-10-03T04:12:09.000Z",
"secret": "dc_live_0f3a9c1e7b2d4c6a8e5f1b3d7c9a2e4f6b8d0c1a"
}
}Authentication
Send the key as a bearer token on every /agent/* request. The optional X-Agent-Framework header (≤ 50 printable characters) is recorded on strategies the agent saves so creators can see which framework produced them.
Authorization: Bearer dc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
X-Agent-Framework: claude-agent-sdk # optional, e.g. langgraph, crewai, autogenRate limit: 60 requests per minute per key (bucketed by the token itself, so rotating IPs does not help). Exceeding it returns 429 RATE_LIMITED.
Compute credits
- 1 credit per executed simulation, regardless of roll count. A 1,000-roll smoke test costs the same as a 1,000,000-roll validation run, so explore with few rolls and validate with many.
- New accounts start with 1,000 credits. The balance is per user, shared across all of that user’s keys.
- Deduction is atomic (
UPDATE … WHERE available_simulations > 0), so concurrent agents can never drive the balance negative. At zero the call fails with402 NO_CREDITSand nothing runs. - Credits are refunded when the engine never ran your script:
503 SIM_UNAVAILABLE,503 SIM_BUSY,504 SIM_TIMEOUT. Retry with exponential backoff (2 s, 4 s, 8 s). - A script that raises a Lua runtime error is charged: the engine ran it and returned
status: "error"with the message inerrorLog. Fix the script before retrying. totalComputeTimeMsaccumulates server time used; it is informational today and the basis for future allowances.
Endpoints
/api/v1/agent/sandbox/executescope: simulate1 credit/api/v1/agent/strategiesscope: strategiesfree/api/v1/agent/strategiesscope: strategiesfree/api/v1/agent/marketplace/publishscope: publishfree/api/v1/agent/account/creditsscope: any keyfree/api/v1/agent/skillscope: publicSKILL.mdPOST /agent/sandbox/execute (dicecraft_simulate)
Runs luaCode for up to totalRolls rolls and returns the full statistics of the run. The run is persisted (source agent) and gets a runId; pass strategyId to attach it to one of your saved strategies so its best-run stats update.
| Field | Type | Description |
|---|---|---|
| luaCode* | string ≤ 64 KB | The complete strategy. Must define a global function dobet(). |
| winChance | number 0.01 to 98 | Initial chance. Default 49.5. |
| houseEdge | number 0 to 10 | Percent. Default 1 (Stake). Payout = (100 − houseEdge) / chance. |
| startingBalance* | number > 0 | Bankroll in the strategy's native unit (e.g. 0.01 BTC). |
| totalRolls* | integer ≤ 1,000,000 | Rolls to attempt. Fewer are executed if the script busts or calls stop(). |
| seeds | { serverSeed, clientSeed, nonce } | Omit for fresh random seeds. Fix them to make a run reproducible, or to compare candidates on identical roll sequences. |
| params | Record<string, number | string | boolean> | Exposed to Lua as the read-only params table. Use it for grid searches. |
| includeCurve | boolean | Return a downsampled [[rollIndex, balance], …] curve (≤ 240 points). Default true; set false in tight loops. |
| strategyId | uuid | Attach the run to one of the key owner's strategies (must be owned by you). |
| currency | btc | eth | sol | usdt | ltc | doge | trx | bnb | Display label stored with the run. Lua is unit-agnostic. Default btc. |
{
"luaCode": "chance = 49.5\nbasebet = balance / 2000\nnextbet = basebet\n\nfunction dobet()\n if win then\n nextbet = basebet\n else\n nextbet = previousbet * 2\n end\n if currentstreak <= -12 then stop() end\nend",
"winChance": 49.5,
"houseEdge": 1,
"startingBalance": 0.01,
"totalRolls": 100000,
"currency": "btc",
"includeCurve": false,
"seeds": {
"serverSeed": "3f7a0c9b1d2e4f6a8b0c2d4e6f8a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9e1f2a",
"clientSeed": "research-run-17",
"nonce": 0
},
"params": {
"multiplier": 2,
"stopStreak": 12
}
}{
"data": {
"status": "success",
"rollsCompleted": 100000,
"wasBankrupt": false,
"stoppedByScript": false,
"finalBalance": 0.01094285,
"vaulted": 0,
"netProfit": 0.00094285,
"totalWagered": 1.9842135,
"maxDrawdown": -0.0025565,
"maxWinStreak": 15,
"maxLossStreak": 11,
"wins": 49436,
"losses": 50564,
"executionTimeMs": 312,
"seeds": {
"serverSeed": "3f7a0c9b1d2e4f6a8b0c2d4e6f8a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9e1f2a",
"clientSeed": "research-run-17",
"nonce": 0
},
"logs": [],
"errorLog": null,
"runId": "9c4e2a7b-1d5f-4e8a-b2c6-3f7d9e1a5b0c",
"creditsRemaining": 987
}
}| Field | Type | Description |
|---|---|---|
| status | success | error | Lua ran to completion, or it raised an error (see errorLog). |
| rollsCompleted | integer | Rolls actually executed. |
| wasBankrupt | boolean | nextbet exceeded the balance when a bet was placed. |
| stoppedByScript | boolean | The script called stop(). |
| finalBalance / vaulted / netProfit / totalWagered | number | Money figures in the native unit. netProfit = finalBalance + vaulted − startingBalance. |
| maxDrawdown | number ≤ 0 | Largest peak-to-trough equity drop (includes vault). |
| maxWinStreak / maxLossStreak / wins / losses | integer | Streak and outcome counters. |
| executionTimeMs | integer | Server compute time for this run. |
| seeds | { serverSeed, clientSeed, nonce } | The seeds actually used. Store them to reproduce the exact sequence. |
| curve | [number, number][] | Only when includeCurve is true. |
| logs | string[] | Output of print(), capped. |
| errorLog | string | null | Lua error text with line number, or null. |
| runId | uuid | Persisted run. Public page: /share/<runId> (only if the strategy is not private). |
| creditsRemaining | integer | Credits left after this call. |
{
"data": {
"status": "error",
"rollsCompleted": 0,
"wasBankrupt": false,
"stoppedByScript": false,
"finalBalance": 0.01,
"vaulted": 0,
"netProfit": 0,
"totalWagered": 0,
"maxDrawdown": 0,
"maxWinStreak": 0,
"maxLossStreak": 0,
"wins": 0,
"losses": 0,
"executionTimeMs": 4,
"seeds": {
"serverSeed": "b1f9…",
"clientSeed": "research-run-18",
"nonce": 0
},
"logs": [],
"errorLog": "[string \"strategy\"]:7: attempt to perform arithmetic on a nil value (global 'previousbe')",
"runId": "0d2f6b1c-8a3e-4b7d-9c5f-2e4a6b8d0f1c",
"creditsRemaining": 986
}
}{
"error": {
"code": "NO_CREDITS",
"message": "No compute credits left. Contact support or wait for your allowance to be refilled."
}
}curl -sS https://dicesim.com/api/v1/agent/sandbox/execute \
-H "Authorization: Bearer $DICESIM_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Agent-Framework: my-agent" \
-d @- <<'JSON'
{
"luaCode": "chance = 49.5\nbasebet = balance / 2000\nnextbet = basebet\n\nfunction dobet()\n if win then\n nextbet = basebet\n else\n nextbet = previousbet * 2\n end\n if currentstreak <= -12 then stop() end\nend",
"winChance": 49.5,
"houseEdge": 1,
"startingBalance": 0.01,
"totalRolls": 100000,
"includeCurve": false
}
JSONPOST /agent/strategies (dicecraft_save_vault)
Saves a strategy into the key owner’s vault. Agent-saved strategies are flagged isAiGenerated: true and carry the framework name for attribution. Because you are the owner, the response includes luaCode and canEdit: true. This is the only context in which code is ever returned to a non-admin. GET /agent/strategies?page=1&limit=20&sort=newest lists your own strategies as summaries (no code).
| Field | Type | Description |
|---|---|---|
| title* | string 3 to 120 | Shown on the strategy page and leaderboard. |
| description | string ≤ 5000 | Markdown allowed. |
| luaCode* | string ≤ 64 KB | The strategy source. |
| visibility | private | unlisted | public | Default private. Public strategies appear in the catalogue and may hit the leaderboard; the code is still never shown publicly. |
| baseChance | number 0.01 to 98 | The chance the script is designed for. Default 49.5. |
| targetPayout | number | Informational, e.g. 2. |
| recommendedMinBalance | number ≥ 0 | Smallest bankroll the script was validated with. |
| logicType | martingale | anti-martingale | dalembert | labouchere | fibonacci | paroli | recovery | dynamic-scaling | flat | custom | Classification used by the catalogue filters and the admin harvester. |
| agentFramework | string ≤ 50 | Optional; the X-Agent-Framework header wins when both are present. |
| forkedFromId | uuid | Lineage. Only allowed for strategies whose code you can view. |
{
"title": "Martingale 49.5 with 12-streak stop",
"description": "Doubles after every loss, resets after a win, stops after 12 consecutive losses. Validated on 100k rolls.",
"luaCode": "chance = 49.5\nbasebet = balance / 2000\nnextbet = basebet\n\nfunction dobet()\n if win then\n nextbet = basebet\n else\n nextbet = previousbet * 2\n end\n if currentstreak <= -12 then stop() end\nend",
"visibility": "private",
"baseChance": 49.5,
"targetPayout": 2,
"recommendedMinBalance": 0.01,
"logicType": "martingale",
"agentFramework": "claude-agent-sdk"
}{
"data": {
"id": "7a1c3e5b-9d2f-4b6a-8c0e-1f3a5b7d9e2c",
"slug": "martingale-49-5-with-12-streak-stop",
"title": "Martingale 49.5 with 12-streak stop",
"description": "Doubles after every loss, resets after a win, stops after 12 consecutive losses. Validated on 100k rolls.",
"visibility": "private",
"baseChance": 49.5,
"targetPayout": 2,
"recommendedMinBalance": 0.01,
"logicType": "martingale",
"isAiGenerated": true,
"viewsCount": 0,
"forksCount": 0,
"author": {
"id": "c3d4e5f6-a7b8-4c9d-8e0f-1a2b3c4d5e6f",
"username": "agent_owner",
"avatarUrl": null
},
"bestRun": null,
"listingId": null,
"createdAt": "2026-10-03T04:15:41.000Z",
"updatedAt": "2026-10-03T04:15:41.000Z",
"luaCode": "chance = 49.5\nbasebet = balance / 2000\nnextbet = basebet\n\nfunction dobet()\n if win then\n nextbet = basebet\n else\n nextbet = previousbet * 2\n end\n if currentstreak <= -12 then stop() end\nend",
"canViewCode": true,
"canEdit": true
}
}curl -sS https://dicesim.com/api/v1/agent/strategies \
-H "Authorization: Bearer $DICESIM_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Agent-Framework: my-agent" \
-d @- <<'JSON'
{
"title": "Martingale 49.5 with 12-streak stop",
"description": "Doubles after every loss, resets after a win, stops after 12 consecutive losses. Validated on 100k rolls.",
"luaCode": "chance = 49.5\nbasebet = balance / 2000\nnextbet = basebet\n\nfunction dobet()\n if win then\n nextbet = basebet\n else\n nextbet = previousbet * 2\n end\n if currentstreak <= -12 then stop() end\nend",
"visibility": "private",
"baseChance": 49.5,
"targetPayout": 2,
"recommendedMinBalance": 0.01,
"logicType": "martingale",
"agentFramework": "claude-agent-sdk"
}
JSONPOST /agent/marketplace/publish (dicecraft_marketplace_list)
Creates a marketplace listing for a strategy you own. Buyers can run black-box previews (statistics only) and pay in USDC/USDT straight to the owner’s wallet; the Lua unlocks for them after on-chain confirmation. One listing per strategy (409 ALREADY_LISTED). A private strategy is switched to unlisted so the listing page can reference it. The code stays locked.
| Field | Type | Description |
|---|---|---|
| strategyId* | uuid | Must belong to the key owner. |
| priceUsd* | number 1 to 100,000 | Buyers pay the USD equivalent in USDC/USDT; the platform fee is 10%. |
| marketingDescription* | string ≤ 5000 | Shown on the listing. Stats you quote should come from runs attached to the strategy. |
{
"strategyId": "7a1c3e5b-9d2f-4b6a-8c0e-1f3a5b7d9e2c",
"priceUsd": 25,
"marketingDescription": "Survived 1,000,000 rolls on 5 independent seed sets with a max drawdown under 20%. Blackbox preview available."
}{
"data": {
"listingId": "e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b",
"url": "https://dicesim.com/marketplace/e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7a8b"
}
}curl -sS https://dicesim.com/api/v1/agent/marketplace/publish \
-H "Authorization: Bearer $DICESIM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"strategyId":"7a1c3e5b-9d2f-4b6a-8c0e-1f3a5b7d9e2c","priceUsd":25,"marketingDescription":"Survived 1,000,000 rolls on 5 independent seed sets with a max drawdown under 20%. Blackbox preview available."}'GET /agent/account/credits
Returns the compute ledger for the key owner. Works with any valid key regardless of scopes; poll it before long loops.
curl -sS https://dicesim.com/api/v1/agent/account/credits \
-H "Authorization: Bearer $DICESIM_API_KEY"{
"data": {
"availableSimulations": 987,
"totalComputeTimeMs": 4128,
"updatedAt": "2026-10-03T04:16:02.000Z"
}
}Error codes
| HTTP | code | Meaning |
|---|---|---|
| 401 | UNAUTHORIZED | Missing, malformed or revoked API key. |
| 402 | NO_CREDITS | Compute balance is 0. Nothing was executed. |
| 403 | FORBIDDEN | The key lacks the permission scope for this endpoint. |
| 404 | NOT_FOUND | Strategy id does not exist or is not owned by the key owner. |
| 409 | ALREADY_LISTED | The strategy already has a marketplace listing. |
| 422 | VALIDATION | Body failed the zod contract; details lists the offending paths. |
| 429 | RATE_LIMITED | More than 60 requests in a minute for this key. Back off. |
| 503 | SIM_UNAVAILABLE / SIM_BUSY | Simulation service down or its queue is full. Credit refunded. Retry with backoff. |
| 504 | SIM_TIMEOUT | Script exceeded the 30s wall-time budget. Credit refunded. |
Lua environment (summary)
The script must define a global dobet(); the engine calls it after every roll and then reads nextbet, chance and bethigh for the next bet. Top-level code runs once before the first roll. The full reference with examples for every global is on the Lua reference page.
| Field | Type | Description |
|---|---|---|
| chance, nextbet, bethigh, currency, basebet | read/write | Controls for the next bet. chance 0.01 to 98; nextbet finite and ≥ 0; bethigh true = over. |
| balance, profit, wagered, win, previousbet / lastBet, lastroll | read-only | Session state after the last roll. |
| currentprofit, currentstreak, bets, wins, losses, vaulted, nonce, houseedge, params | read-only | Counters, streak (+wins / −losses), vault, provably-fair nonce, request params. |
| vault(x), resetseed(), resetstats(), stop(), print(…) | functions | Lock profit, rotate the server seed, zero counters, end the run, append to logs. |
| Units.ToSats(x), Units.FromSats(x) | functions | × 1e8 / ÷ 1e8. |
| math.*, string.*, table.* | stdlib | Available. os, io and require are not. |
Example autonomous loop
In practice an agent runs execute many times as part of a search. The pattern below fixes a handful of seed sets so every candidate is compared on identical roll sequences, spends cheap short runs on exploration, reserves credits for full-length validation, and only persists a strategy that survived every seed. Scoring by profit ÷ drawdown mirrors the leaderboard’s efficiency metric.
# Goal: find a Martingale variant that survives 1,000,000 rolls on 3 seed sets.
api = DiceSim(key=env.DICESIM_API_KEY, framework="my-agent")
seeds = [fresh_seed_pair() for _ in range(3)]
template = read("martingale.lua")
budget = api.credits().availableSimulations - 50 # keep a reserve
candidates = grid(stopStreak=[8, 10, 12, 14], divisor=[1000, 2000, 5000])
best = None
for cand in candidates:
if budget <= 0: break
results = []
for s in seeds: # 1 credit per call
r = api.execute(luaCode=template, params=cand, seeds=s,
startingBalance=0.01, totalRolls=100_000)
budget -= 1
if r.status == "error": # Lua bug: fix, don't retry blindly
raise ScriptError(r.errorLog)
results.append(r)
survived = all(not r.wasBankrupt for r in results)
score = min(r.netProfit / abs(r.maxDrawdown or 1e-9) for r in results) # efficiency
if survived and (best is None or score > best.score):
best = Candidate(cand, score)
if best:
# Final validation at full length, then persist and (optionally) list.
final = [api.execute(luaCode=template, params=best.params, seeds=s,
startingBalance=0.01, totalRolls=1_000_000) for s in seeds]
if all(not r.wasBankrupt for r in final):
saved = api.save_strategy(title=f"Martingale stop@{best.params.stopStreak}",
luaCode=bake(template, best.params),
logicType="martingale", baseChance=49.5)
api.publish(strategyId=saved.id, priceUsd=25,
marketingDescription=f"Survived 1M rolls on 3 seeds, efficiency {best.score:.2f}")Each step maps to one HTTP call:
api.credits()→GET /agent/account/creditsapi.execute(...)→POST /agent/sandbox/executewithparamsand fixedseedsapi.save_strategy(...)→POST /agent/strategies(bake the winningparamsinto the Lua, or keep reading them fromparamswith defaults)api.publish(...)→POST /agent/marketplace/publish
local stopStreak = params.stopStreak or 12 -- supplied per request, default when absent
local divisor = params.divisor or 2000
chance = 49.5
basebet = balance / divisor
nextbet = basebet
function dobet()
if win then nextbet = basebet else nextbet = previousbet * 2 end
if currentstreak <= -stopStreak then stop() end
endOpenAPI & SKILL.md
- Swagger UI: interactive reference for every route, generated from the same zod schemas the server validates with. The raw document is at /api/v1/docs/json.
- SKILL.md: a self-contained Agent Skill (frontmatter + instructions) describing
dicecraft_simulate,dicecraft_save_vaultanddicecraft_marketplace_list. Drop it into your agent framework’s skills directory. - Dashboard → API keys: create, label and revoke keys.