Home

Blobfish API v1 · /api/v1/sandbox

Generate, run, and verify synthetic RL worlds over HTTP.

This is the same API the Blobfish sandboxruns on. One prompt generates a relational, executable world — SQLite tables, Python tools, grounded tasks, and deterministic VCode verifiers — hosted at a stable worldId. Then drive its tasks with an agent and score the real state changes. No LLM judge in the reward path.

Base URL https://blobfish.ai
Free API key in one call · anonymous access for quick tests
Every task scored by executable VCode over real before/after state
39 endpoints across 7 categories

Getting Started

Generate a world, act in it, and score the result in eight steps. Commands run against https://blobfish.ai. Endpoints that depend on external infrastructure (RunPod deploy, Composio research, GPU training) return an explicit not_configured status when unavailable. Get a free API key for higher rate limits and world ownership, or try anonymously (10 generations/day).

1

Get an API Key (optional)

Anonymous access works for exploration (10 gen/day). For sustained use, get a free key — gives 10 gen/day and scoped world ownership.

POST /api/v1/auth/keys
curl -sX POST https://blobfish.ai/api/v1/auth/keys \
  -H 'Content-Type: application/json' \
  -d '{"name":"my-key","email":"you@example.com"}'

# Returns: { "key": "bf_626c4b...", "tier": "free", "rate_limits": {...} }
# Then pass either header on all subsequent requests:
#   -H 'X-API-Key: bf_626c4b...'
#   -H 'Authorization: Bearer bf_626c4b...'
2

Generate a World

Send a domain prompt and get back a full executable world with tables, tools, tasks, and verifiers.

POST /api/v1/sandbox/jobs
curl -sX POST https://blobfish.ai/api/v1/sandbox/jobs \
  -H 'Content-Type: application/json' \
  -d '{"prompt":"law firm matter intake operations"}'

# Returns: { "job_id": "job_e78af19041f046eb", "status": "creating_world" }
3

Poll Until Ready

Poll the job endpoint or connect to the SSE stream. Every job terminates in exactly one of "ready" or "failed" (with job.error naming the failing stage) — a server-side watchdog fails any job with no progress for 8 minutes, so no job builds forever.

GET /api/v1/sandbox/jobs/{jobId}
# Option A: Poll
curl -s https://blobfish.ai/api/v1/sandbox/jobs/job_e78af19041f046eb
# Returns: { "job": { "status": "ready", "world_id": "sbx_..." } }
# On failure: { "job": { "status": "failed", "error": "Stage \"...\" failed: ..." } }

# Option B: SSE stream
curl -sN https://blobfish.ai/api/v1/sandbox/jobs/job_e78af19041f046eb/stream
# data: {"type":"progress","phase":"research","detail":"Discovering tools..."}
# data: {"type":"done","status":"ready","world_id":"sbx_..."}
4

Inspect the World

Retrieve the full world: thesis, SQLite tables with seeded rows, executable Python tools, grounded tasks, and VCode verifiers.

GET /api/v1/sandbox/worlds/{worldId}
curl -s https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc \
  | python3 -m json.tool | head -40

# thesis.company, thesis.domain, thesis.roles
# tables[].name, tables[].columns, tables[].sample_rows
# tools[].name, tools[].type, tools[].source
# tasks[].task_id, tasks[].prompt, tasks[].required_tools
# verifiers[].task_id, verifiers[].assertions, verifiers[].vcode
5

Run an Agent Task

An agent drives a task against live SQLite. The VCode verifier scores real before/after state and emits a binary reward.

POST /api/v1/sandbox/worlds/{worldId}/run-task
curl -sX POST https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc/run-task \
  -H 'Content-Type: application/json' \
  -d '{"task_id":"task_001"}'

# Returns: { "passed": true, "reward": 1, "runtime": "local",
#   "steps": [...], "verifier": { "passed": true, "assertions": [...] } }
6

Check Quality

Audit the world for degenerate tasks, grounding coverage, difficulty distribution, and creation-calibration rollout explanations. Quality is a hard gate, not a report: degenerate rewards, missing research coverage, Quick Preview provenance, and out-of-band calibration block benchmarks, MCP-Mark, and queue-for-training (422) until fixed.

GET /api/v1/sandbox/worlds/{worldId}/quality
curl -s https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc/quality

# Returns: { "quality": { "degenerate_tasks": 0, "total_tasks": 14,
#   "grounding_coverage": { "coverage": 0.833 },
#   "difficulty": { "easy": 4, "medium": 6, "hard": 4 } },
#   "validity": { "training_ready": true, "blockers": [] },
#   "creation_calibration": { "summary": {...}, "tasks": [{"pass_rate": 0.5, "variance": {...}}] } }
7

Download Harbor Package

Download the self-contained tar archive with schema, tools, verifiers, tasks, Dockerfile, and recorded traces.

GET /api/v1/sandbox/worlds/{worldId}/download
curl -s https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc/download \
  -o world.tar

tar xf world.tar -C my-world/
# world.json, server.py, create_db.py, tools.py, Dockerfile
8

Deploy & Run

Build the Harbor image locally or deploy to RunPod for hosted evaluation.

POST /api/v1/sandbox/worlds/{worldId}/deploy
# Option A: Run locally
cd my-world && docker build -t world . && docker run -p 8080:8080 world

# Option B: Deploy to RunPod
curl -sX POST https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc/deploy
# Returns: { "status": "deployed", "url": "https://api.runpod.ai/v2/ep_abc" }

Endpoints

39 endpoints grouped into 7 categories. Click Try it on any endpoint to send a live request from this page.

Create executable RL worlds from a natural-language prompt.

POST/api/v1/sandbox/generate

Synchronous Quick Preview generation for anchored prototypes. Blocks until the world is ready and returns it directly, but stamps the result as quick_preview_ungrounded_prototype and not training-ready. Requires PRD/API/SOP anchor_files; use POST /api/v1/sandbox/jobs with mode=deep for production quality gates. Send {"stream":true} for SSE progress events.

Request Body

FieldTypeRequiredDescription
promptstringYesDomain description, e.g. "law firm matter intake"
anchor_filesarrayYesUploaded anchor documents with filename/content, e.g. PRD markdown or OpenAPI JSON
streambooleanNoIf true, returns a Server-Sent-Events stream of generation stages instead of blocking

Response

{
  "world_id": "sbx_9f3aa4bc6f684ff3",
  "thesis": {
    "company": "Meridian Legal Partners",
    "domain": "legal",
    "vertical": "legal_services",
    "roles": [
      "partner",
      "associate",
      "paralegal"
    ]
  },
  "tables": [
    {
      "name": "matters",
      "columns": [
        "id",
        "title",
        "status",
        "assigned_to"
      ],
      "row_count": 12
    }
  ],
  "tools": [
    {
      "name": "open_matter",
      "type": "write",
      "target_tables": [
        "matters"
      ]
    }
  ],
  "tasks": [
    {
      "task_id": "task_001",
      "prompt": "Open matter MAT-2024-0031 and assign to associate Chen",
      "required_tools": [
        "open_matter"
      ]
    }
  ],
  "verifiers": [
    {
      "task_id": "task_001",
      "assertions": [
        "matters_mat_2024_0031_status_is_open",
        "matters_mat_2024_0031_assigned_to_is_chen",
        "no_collateral_matters",
        "reads_before_writes"
      ]
    }
  ]
}

curl

curl -sX POST https://blobfish.ai/api/v1/sandbox/generate \
  -H 'Content-Type: application/json' \
  -d '{"prompt":"law firm matter intake operations","anchor_files":[{"filename":"matter-intake-prd.md","content":"# Matters Schema\n- title: string\n- status: enum open closed\n# Clients Schema\n- name: string\n- email: string"}]}'
POST/api/v1/sandbox/jobs

Async job-based generation. Sandbox jobs always use the Research-backed staged path: the job must execute research, collect evidence sources, pass creation scorecard gates, and satisfy a creation-time complexity check before it is marked ready. This step-budget proxy is not model calibration; benchmark and training use require measured model rollouts. Returns a job_id immediately while the world builds in the background.

Request Body

FieldTypeRequiredDescription
promptstringYesDomain description for world generation
modestringNoIgnored by sandbox job creation; jobs always run Research-backed staged generation.
target_failure_ratenumberNoDesired baseline failure rate, e.g. 0.5 for maximum training signal
anchorsarrayNoUploaded docs, URLs, PRDs, API specs, or database schemas used as grounding evidence

Response

{
  "job_id": "job_e78af19041f046eb",
  "status": "creating_world",
  "stages": [
    {
      "key": "create_world",
      "label": "Creating world",
      "status": "running"
    },
    {
      "key": "make_package",
      "label": "Making executable package",
      "status": "pending"
    },
    {
      "key": "launch_sandbox",
      "label": "Launching sandbox",
      "status": "pending"
    },
    {
      "key": "run_first_test",
      "label": "Running first agent test",
      "status": "pending"
    }
  ]
}

curl

curl -sX POST https://blobfish.ai/api/v1/sandbox/jobs \
  -H 'Content-Type: application/json' \
  -d '{"prompt":"law firm matter intake operations","mode":"deep","target_failure_rate":0.5}'
GET/api/v1/sandbox/jobs/{jobId}

Poll a build job. While running returns live stage progress; once status is "ready" the response includes the full world. TERMINAL-STATE CONTRACT: every job ends in exactly one of "ready" or "failed" — never an infinite build. On "failed", job.error names the failing stage and reason (world-generation, package, sandbox-launch, first-test, or validation failure). A server-side watchdog reconciles jobs with no progress for 8 minutes to "failed" (generation process died), so a job can never stay non-terminal forever.

Response

{
  "job": {
    "job_id": "job_e78af19041f046eb",
    "status": "ready",
    "world_id": "sbx_9f3aa4bc6f684ff3",
    "world": {
      "thesis": {
        "company": "Meridian Legal Partners",
        "domain": "legal"
      },
      "tables": [
        {
          "name": "matters",
          "columns": [
            "id",
            "title",
            "status"
          ]
        }
      ],
      "tools": [
        {
          "name": "open_matter",
          "type": "write"
        }
      ],
      "tasks": [
        {
          "task_id": "task_001",
          "prompt": "..."
        }
      ]
    }
  }
}

curl

curl -s https://blobfish.ai/api/v1/sandbox/jobs/job_e78af19041f046eb
GET/api/v1/sandbox/jobs/{jobId}/stream

SSE progress stream for a build job. Emits stage transitions, pipeline phase progress, thesis previews, and a terminal done/error event.

Response

data: {"type":"stage","stage":"creating_world"}

data: {"type":"progress","stage":"create_world","phase":"research","detail":"Discovering MCP tools...","iteration":1,"maxIterations":3}

data: {"type":"thesis_preview","thesis":{"company":"Meridian Legal","domain":"legal"}}

data: {"type":"done","status":"ready","world_id":"sbx_9f3aa4bc6f684ff3"}

# Failure is equally terminal — never an infinite stream:
# data: {"type":"done","status":"failed","error":"Stage \"Creating world\" failed: ..."}
# data: {"type":"timeout"}  (stream safety-closes after 5 minutes)

curl

curl -sN https://blobfish.ai/api/v1/sandbox/jobs/job_e78af19041f046eb/stream
GET/api/v1/sandbox/jobs

List recent build jobs for your session (identified by HttpOnly cookie).

Response

{
  "jobs": [
    {
      "job_id": "job_e78af19041f046eb",
      "status": "ready",
      "world_id": "sbx_9f3aa4bc6f684ff3",
      "prompt": "law firm matter intake"
    },
    {
      "job_id": "job_a1b2c3d4e5f60718",
      "status": "creating_world",
      "prompt": "dental clinic scheduling"
    }
  ]
}

curl

curl -s https://blobfish.ai/api/v1/sandbox/jobs
POST/api/v1/sandbox/worlds/import

Import an externally built world. Two modes: (1) JSON body (Content-Type: application/json) imports a SandboxWorld-shaped payload into the sandbox runtime. Imported JSON worlds are stamped as app-mirror/replay imports and are not training-ready; use /api/v1/sandbox/jobs mode=deep for Research-backed training worlds. (2) Multipart tar.gz (Content-Type: multipart/form-data) imports a Blobfish download package into the hosted world store for evaluation, chat, and run-task workflows. Use ?target=hosted or ?target=sandbox to choose the store (defaults: JSON=sandbox, multipart=hosted). The archive must contain world.json, tools.json, tasks.json and optionally environment.db, schema.sql, seed.sql, personas.json, env_spec.json. Max 24 MB JSON / 64 MB tar.gz.

Request Body

FieldTypeRequiredDescription
tablesarrayYes[{name, columns:[{name,type,pk?,fk?}], sample_rows:[...]}] -- rows seed the live SQLite (JSON mode)
toolsarrayYes[{name, type, source, parameters, target_tables}] -- source must define `def <name>(db_path, ...)` (JSON mode)
tasksarrayNo[{task_id, prompt, required_tools, ...}] -- extra provenance fields (expected_calls) ride through (JSON mode)
verifiersarrayNo[{task_id, vcode}] -- vcode must define verify(initial_state, final_state, trace); every task needs one (JSON mode)
thesisobjectNo{company, domain, vertical, ...} shown in the workspace header (JSON mode)
worldfileNotar.gz archive of the mirror world directory (multipart mode)
labelstringNoHuman-readable label for the imported world (multipart mode)

Query Parameters

ParamTypeRequiredDescription
targetstringNo'hosted' or 'sandbox' -- which store receives the import (default: sandbox for JSON, hosted for multipart)

Response

{
  "world_id": "sbx_1767ddbd5dcd",
  "url": "/sandbox?world=sbx_1767ddbd5dcd",
  "target": "sandbox",
  "counts": {
    "tables": 36,
    "rows": 745,
    "tools": 113,
    "tasks": 11,
    "verifiers": 11
  },
  "warnings": []
}

curl

# JSON import (sandbox):
curl -sX POST https://blobfish.ai/api/v1/sandbox/worlds/import \
  -H 'Content-Type: application/json' --data-binary @world_payload.json

# tar.gz import (hosted world store):
tar czf world.tar.gz -C ./blobfish_worlds env_myapp_mirror_x
curl -sX POST https://blobfish.ai/api/v1/sandbox/worlds/import \
  -H 'X-API-Key: bf_yourkey' \
  -F world=@world.tar.gz -F label="My App Mirror"

# JSON import into hosted store:
curl -sX POST "https://blobfish.ai/api/v1/sandbox/worlds/import?target=hosted" \
  -H 'Content-Type: application/json' --data-binary @world_payload.json

Notes

Authentication

Anonymous access works for exploration (10 gen/day, 60 calls/hour). For production use, create a free API key via POST /api/v1/auth/keys and pass it as X-API-Key header or Authorization: Bearer bf_... header. Free tier: 10 gen/day, 100 calls/hour. Worlds created with a key are tracked and retrievable via ?mine=true.

LLM-Optional

Generation is prompt-driven and LLM-optional: with a provider key the domain model is richer, otherwise a deterministic heuristic pipeline still produces a runnable world.

Persistence

Worlds persist per server instance. Set BLOBFISH_SANDBOX_DIR to a durable mount for cross-instance access. Worlds are also downloadable as Harbor archives.

Verification

Every task is scored by executable VCode over real before/after SQLite state. No LLM judge in the reward path. Verifiers use deterministic state-diff assertions.

Honesty Policy

The API honestly reports its capabilities. Benchmarks with no in-repo harness return null. RunPod returns not_configured when unavailable. GRPO regressions are disclosed.

Rate Limits

Anonymous: 10 gen/day, 60 API calls/hour. Free key: 10 gen/day, 100 calls/hour. Pro: 100/1000. Enterprise: 1000/10000. Exceeded? 429 with Retry-After header.

Error Responses

All errors return JSON with an error field. Common: 404 world not found,429 rate limit exceeded,400 missing required fields,500 generation timeout or VCode execution error.

Cookbook: Attach your production agent over HTTP

Create a key, fork an isolated session for the mirrored sandbox world, then point any MCP-over-HTTP client at the returned mcp_url. Pass both the API key andX-Blobfish-Session on every call so repeated or parallel runs do not share state. For assessment and CI runs, also pass X-Blobfish-Task,X-Blobfish-Run, and X-Request-Id so the report can attribute every tool call to a task.

  • MCP over HTTP: use the returned mcp_url.
  • Blobfish JSON transport: use the returned tool_calls_url.
  • OpenAI-compatible agent endpoint: use blobfish eval --policy agent.
  • Vendor/internal REST base-URL swapping: use tools/vendor_rest_shim with a route map.
# 1. Create an API key
curl -sX POST https://blobfish.ai/api/v1/auth/keys \
  -H 'Content-Type: application/json' \
  -d '{"name":"ci-agent","email":"agent@example.com"}'

# 2. Create a session for one sandbox world
curl -sX POST https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc6f684ff3/sessions \
  -H 'X-API-Key: bf_YOUR_KEY'
# → { "session_id": "sess_...", "mcp_url": ".../mcp", "tool_calls_url": ".../tool-calls" }

# 3. Use HTTP MCP against the session fork
curl -sX POST https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc6f684ff3/mcp \
  -H 'X-API-Key: bf_YOUR_KEY' \
  -H 'X-Blobfish-Session: sess_...' \
  -H 'X-Blobfish-Task: task_...' \
  -H 'X-Blobfish-Run: ci-2026-07-08' \
  -H 'X-Blobfish-Run-Mode: ci' \
  -H 'X-Request-Id: req_001' \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Reset between runs with POST /api/v1/sandbox/worlds/<world_id>/sessions/<session_id>/reset. For REST-only agents, run python tools/vendor_rest_shim/vendor_rest_shim.py --config route-map.jsonand point the vendor base URL at the shim.

Cookbook: Regression-test your agent in CI

Use the Blobfish CLI to gate PRs on agent quality. Three commands:

# 1. Export your world's tasks as an eval dataset
blobfish export-eval ./my_world --format jsonl
# → eval.jsonl: {task_id, split, difficulty, instruction, expected_calls, verifier_ref}

# 2. Grade your agent (or use oracle/random baselines)
blobfish eval ./my_world --policy agent --agent-endpoint http://localhost:3000/api/agent --run v1

# 3. Gate on regressions (exit 1 = fail the build)
blobfish ci-gate ./my_world \
  --policy agent \
  --agent-endpoint http://localhost:3000/api/agent \
  --hosted-world-url https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc6f684ff3 \
  --api-key bf_YOUR_KEY \
  --min-pass-rate 0.7 \
  --no-regressions \
  --baseline baseline.json \
  --write-baseline current-baseline.json \
  --langfuse-dataset blobfish-pr \
  --langfuse-run "$GITHUB_SHA"

Optional: push eval datasets to Langfuse with --format langfuse (requiresLANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY). Omit --hosted-world-url to run the same agent loop against the local CLI harness instead of hosted sandbox sessions. Use the Blobfish download package workflow for moving a generated world into the hosted sandbox, and app-mirror for cloning your production app into a testable mock world.

Cookbook: Cheaper-model ROI POC

Use validated customer traces for SFT first, then serve the adapter, run A/B eval, and compute cost from measured usage. Training result fields stay nulluntil a real eval measures them.

blobfish distill-data \
  --traces ./traces.jsonl \
  --world ./my_world \
  --out ./sft.jsonl \
  --validate replay \
  --report-out ./distill_report.json

blobfish train \
  --world ./my_world \
  --data ./sft.jsonl \
  --base Qwen/Qwen3-8B \
  --method sft \
  --target local-mlx \
  --out ./output/train

blobfish serve-adapter \
  --adapter ./output/train/adapter \
  --base Qwen/Qwen3-8B \
  --target local \
  --registry-url https://model-registry.example.com

blobfish eval-ab \
  --world ./my_world \
  --baseline-run frontier \
  --agent-endpoint http://127.0.0.1:8000/v1/chat/completions

blobfish cost-report \
  --world ./my_world \
  --baseline-run frontier \
  --eval-run agent-... \
  --frontier-price 5.00 \
  --tuned-serving-cost 0.30 \
  --training-cost 25.00 \
  --usage-jsonl ./usage.jsonl