Home

Blobfish API v1 · /api/v1/sandbox

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

This is the same API the Blobfish Studio runs 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
63 endpoints across 11 categories

Environments — five calls, start here

You want a simulation environment where your agent practices long-horizon work — ERP, CRM, spreadsheet ops — and gets measurably better. /api/v1/environments is that path in five calls: create a world that mirrors the customer’s system, get every mock tool as a callable HTTP endpoint with OpenAI and Anthropic schemas already formatted, isolate state per eval run, and score with an executable verifier. Your production agent code does not need an MCP client, a JSON-RPC envelope, or any knowledge of our world model.

mode="deep" builds the research-backed world (anchors, tool-survival and task-acceptance gates — minutes).mode="preview" returns an ungrounded prototype in seconds for wiring up a harness; it is not production- or training-grade, and every response says so in its contract field. Both modes share one env_id and one polling contract, so switching is a one-word change.

# 1. Create a world that mirrors the customer's system.
curl -sX POST https://blobfish.ai/api/v1/environments \
  -H 'X-API-Key: bf_YOUR_KEY' -H 'Content-Type: application/json' \
  -d '{"prompt":"ERP for a mid-size industrial distributor: purchase orders, inventory receipts, three-way invoice match","mode":"deep"}'
# -> 202 {"env_id":"job_9f2c4b18aa","status":"building", ...}

# 2. Poll the SAME handle until the mock tools exist (one contract for both modes).
curl -s https://blobfish.ai/api/v1/environments/job_9f2c4b18aa -H 'X-API-Key: bf_YOUR_KEY'
# -> {"status":"ready","tools":[...],"openai_tools":[...],"anthropic_tools":[...],"tasks":[...]}

# 3. Isolate state for ONE eval rollout.
curl -sX POST https://blobfish.ai/api/v1/environments/job_9f2c4b18aa/sessions \
  -H 'X-API-Key: bf_YOUR_KEY' -H 'Content-Type: application/json' -d '{}'
# -> 201 {"session_id":"sess_4d9a1c77b0e3f2a5"}

# 4. Call a mock tool — POST the model's tool-call arguments verbatim.
curl -sX POST https://blobfish.ai/api/v1/environments/job_9f2c4b18aa/tools/approve_purchase_order \
  -H 'X-API-Key: bf_YOUR_KEY' -H 'X-Blobfish-Session: sess_4d9a1c77b0e3f2a5' \
  -H 'Content-Type: application/json' -d '{"order_id":4182,"approver":"j.reyes"}'

# 5. Score the rollout with the task's executable verifier (no LLM judge).
curl -sX POST https://blobfish.ai/api/v1/environments/job_9f2c4b18aa/verify \
  -H 'X-API-Key: bf_YOUR_KEY' -H 'X-Blobfish-Session: sess_4d9a1c77b0e3f2a5' \
  -H 'Content-Type: application/json' -d '{"task_id":"task_approve_blocked_po"}'
# -> {"passed":true,"reward":1,"verified_by":"vcode","assertions":[...]}

Sample Python project

A complete, standalone project that creates a world and evaluates a production agent against its mock tools with Langfuse reporting lives at demo/frontier-lab-eval/. One dependency (httpx); the client is a single readable file.

# pip install httpx
from blobfish_eval import BlobfishClient, run_eval

with BlobfishClient(api_key="bf_YOUR_KEY") as client:
    env = client.create_environment(
        "ERP for a mid-size industrial distributor: purchase orders, "
        "inventory receipts, three-way invoice match, credit-hold approvals",
        mode="deep",          # "preview" = ungrounded prototype in seconds
    )
    env.wait_until_ready()    # same polling contract for both modes

    print(run_eval(env).summary())
    # environment : job_9f2c4b18aa  (mode=deep)
    # agent       : anthropic/claude-sonnet-5
    # pass rate   : 11/18  (61%)
    # mean reward : 0.611
    # langfuse    : https://cloud.langfuse.com/datasets/blobfish-job_9f2c4b18aa

Swap in your own agent — it is the only file you need to touch. Tool calls are a plain JSON POST of the model’s arguments object:

# Your production agent plugs in here — nothing else changes.
class MyProductionAgent:
    provider = "acme"
    model = "acme-planner-v7"

    def run(self, task, tools, session):
        # tools   -> name, JSON Schema, mutates flag, callable URL
        # session -> isolated state for THIS rollout
        response = anthropic.Anthropic().messages.create(
            model="claude-sonnet-5",
            max_tokens=2048,
            messages=[{"role": "user", "content": task.prompt}],
            tools=env.anthropic_tools,      # paste them straight in
        )
        for block in response.content:
            if block.type == "tool_use":
                session.call_with(block.name, dict(block.input))
        ...

run_eval(env, agent=MyProductionAgent())

One session per rollout. A Langfuse dataset run executes many items concurrently; without a session per item they all mutate the same rows and every verifier result after the first scores someone else’s writes — the run stops being reproducible without ever looking broken.

# Each task runs in its OWN session, so concurrent rollouts never collide.
# Scores pushed to Langfuse are the environment's executable verifier output.
export LANGFUSE_PUBLIC_KEY=pk-lf-...  LANGFUSE_SECRET_KEY=sk-lf-...
export ANTHROPIC_API_KEY=sk-ant-...

python run_eval.py --mode deep --agent anthropic --workers 8

#   PASS  task_approve_blocked_po   reward=1.00  turns=5
#   FAIL  task_three_way_match      reward=0.00  turns=12
# -> dataset run with per-task traces + blobfish_reward / blobfish_passed scores

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 Oracle-admitted Harbor Tasks

Download official Harbor 1.4 task directories only after their oracle solutions earned reward 1.0. A 409 response means the release is still being gated; the endpoint never substitutes the legacy bundle.

GET /api/v1/sandbox/worlds/{worldId}/download
curl -s 'https://blobfish.ai/api/v1/sandbox/worlds/sbx_9f3aa4bc/download?format=harbor' \
  -o harbor-tasks.tar.gz

mkdir harbor-release && tar xzf harbor-tasks.tar.gz -C harbor-release/
cd harbor-release
docker build -t blobfishai/world-sbx_9f3aa4bc:local world-image
uvx harbor run -p tasks -a oracle
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" }

Claude Code Skill — local practice worlds

No hosted job needed. The blobfish skill turns any Claude-Code-driven repo into a local practice ground: generate a world for your vertical (SQLite database + validated tools + verifiable tasks + personas, split practice/heldout), serve it over stdio MCP, practice tasks with executable-verifier rewards, and distill what worked into a skill for your repo. Fully offline — the keyless engine needs no LLM. Prefer clicking to typing? Build in the browser from the portal.

1

Get the repo

The skill ships inside the blobfishai monorepo (requires repo access). Python 3.12+ on your machine; no API keys needed for generation.

git clone
git clone git@github.com:blobfishai/blobfishai.git ~/workplace/blobfishai
2

Install the skill into your repo

Copies the skill into your repo's .claude/skills/blobfish and pins BLOBFISH_HOME. Use --link instead to symlink (auto-updates with the checkout). Dev-time shell-out only — your repo gains no runtime dependency.

skills/blobfish/install.sh
bash ~/workplace/blobfishai/skills/blobfish/install.sh /path/to/your-repo
# → installed blobfish skill → /path/to/your-repo/.claude/skills/blobfish
3

Health check

Verifies Python, the checkout, and every pipeline import. Run this first whenever something misbehaves.

blobfish doctor
bash /path/to/your-repo/.claude/skills/blobfish/scripts/blobfish doctor
# python: 3.12.x ✓ · blobfish home ✓ · imports ✓ · doctor: OK
4

Generate a world for your vertical

Built-in offline presets: wonson_erp (trading ERP + channel order import), pmi_labor_agency (CPMI case management), shoebox_manufacturing (make-to-order production). Or pass --brief naming >=3 concrete entities. These are local practice/eval worlds; production training worlds require Research-backed hosted jobs with grounding and calibration gates.

blobfish generate
cd /path/to/your-repo
bash .claude/skills/blobfish/scripts/blobfish generate \
  --vertical shoebox_manufacturing --tenant acme --out ./blobfish_worlds
# world: env_acme_shoebox_manufacturing_…  · 11 tables · 24 tools · tasks split train/heldout
5

Connect your agent over MCP

The stdio MCP server exposes the world's tools plus the verified-episode lifecycle: task_list → task_start → (tool calls on the episode's scratch DB) → task_verify. Rewards are executable VCode — no LLM judge.

claude mcp add
claude mcp add blobfish-world -- \
  bash /path/to/your-repo/.claude/skills/blobfish/scripts/blobfish \
  serve /path/to/your-repo/blobfish_worlds/<world_dir> --run training-day1
6

Practice, distill, measure

Baseline first (oracle ≈ ceiling, random ≈ floor), practice the local split over MCP, distill verified successes into a draft skill for your repo, then re-eval on the heldout split and compare runs. Queue model training only through the hosted Research-backed sandbox training gates.

blobfish eval · distill · report
BF=.claude/skills/blobfish/scripts/blobfish
bash $BF eval  ./blobfish_worlds/<world_dir> --policy oracle --run oracle-base
bash $BF eval  ./blobfish_worlds/<world_dir> --policy random --run random-base
# …practice over MCP under --run training-day1, then:
bash $BF distill ./blobfish_worlds/<world_dir> --run training-day1 --out ./drafts
bash $BF report  ./blobfish_worlds/<world_dir> --compare training-day1,training-day2-with-skill

Hosted company gyms

The hosted API exposes both Quick Preview worlds and Research-backed sandbox jobs. Use POST /api/v1/worlds for evaluation previews, or POST /api/v1/sandbox/jobs with mode=deep for production-quality training candidates with grounding, quality, and calibration gates — then fork one isolated session per rollout.

1

Choose preview or deep company gym

Quick Preview is a fast shared demo surface, not an isolated concurrent benchmark. For reproducible training, create one Research-backed deep world for the company and add departments and scenarios to that same company family.

POST /api/v1/worlds · POST /api/v1/sandbox/jobs
# Fast shared preview
curl -sX POST https://blobfish.ai/api/v1/worlds \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: bf_…' \
  -d '{"prompt":"shoebox manufacturer work orders and QC"}'
# → { "worldId": "…", "links": { "mcp": "…" } }

# Deep composite company world (asynchronous)
curl -sX POST https://blobfish.ai/api/v1/sandbox/jobs \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: bf_…' \
  -H 'Idempotency-Key: northstar-mutual-v1' \
  -d '{"mode":"deep","company_type_key":"insurance_carrier","company_instance_key":"northstar_mutual","prompt":"Northstar Mutual: sales, claims, underwriting, HR, finance, and compliance workflows"}'
2

Wait for the immutable company revision

Poll the durable job. Success returns a world_id only after the exact revision is saved and, for public company gyms, verified and preloaded behind the warm gateway. Do not submit another world for the same company; regenerate the existing family to add scenarios.

GET /api/v1/sandbox/jobs/{jobId}
curl -s https://blobfish.ai/api/v1/sandbox/jobs/<jobId> \
  -H 'X-API-Key: bf_…'
# → { "status":"succeeded", "world_id":"sbx_…", ... }
3

Fork isolated rollout state

Create one session per agent rollout. This copies the preloaded baseline instead of recompiling the company, so parallel agents get independent accounts and mutations without a cold start.

POST /api/v1/sandbox/worlds/{worldId}/sessions
curl -siX POST https://blobfish.ai/api/v1/sandbox/worlds/<worldId>/sessions \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: bf_…' \
  -d '{}'
# → Mcp-Session-Id: sess_…
# → { "session_id":"sess_…", "mcp_url":"…/mcp" }
4

Connect any HTTP MCP agent

Use the returned stable mcp_url with the API key and standard MCP session header. initialize, tools/list, tools/call, resources/list, and resources/read all operate on this session only.

POST /api/v1/sandbox/worlds/{worldId}/mcp
curl -sX POST <mcp_url> \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: bf_…' \
  -H 'Mcp-Session-Id: sess_…' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
5

Reset and repeat reproducibly

Reset the same session to its company/scenario baseline between attempts, or create separate sessions for GRPO groups. The published revision stays immutable while rollout state changes independently.

POST /api/v1/sandbox/worlds/{worldId}/sessions/{sessionId}/reset
curl -sX POST https://blobfish.ai/api/v1/sandbox/worlds/<worldId>/sessions/<sessionId>/reset \
  -H 'X-API-Key: bf_…'

Endpoints

63 endpoints grouped into 11 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. Send an Idempotency-Key on any compute-spending request that may be retried; the same owner/key/request returns the original job, while reusing the key for different content returns 409.

Request Body

FieldTypeRequiredDescription
promptstringYesDomain description for world generation
company_instance_keystringNoStable company identity within the caller's tenant. Reuse the key to expand one company; change it for isolated mock data with the same company-type tool archetype. Named instances are owner-listed and direct-link shareable, not published as duplicate public archetypes.
target_failure_ratenumberNoDesired baseline failure rate, e.g. 0.5 for maximum training signal
requested_task_countnumberNoAdvanced exact-count build (2–100). The job is not ready until every generated task has a verifier and one real deepseek-v4-flash/VCode result. For resumable 100-task customer packaging, the customer-release endpoints below are preferred.
anchor_filesarrayNoInline uploaded files as {filename, content, content_type?}: PRDs, API specs, schemas, policies, traces, or source samples
mcp_appsarrayNoMCP/Composio app selections with optional tool schemas
mock_servicesarrayNoProduction-service twins to compile, each with service, optional data_seed_id, and tool_limit
freshbooleanNoBuild a fresh immutable revision rather than opening a reusable default world

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"
    }
  ]
}

Errors

codestatusretryablemeaning
company_control_plane_unavailable503yesThe one-active-revision preflight read did not finish inside its 3s budget. The endpoint fails CLOSED rather than risk enqueueing two expensive builds for one company, so this is a retry signal and not a failed build. Honour `retry-after` (2s) and retry the identical request; sending an Idempotency-Key makes the retry safe. Expect this under control-plane load: it was answering roughly half of production create calls on 2026-08-04.
company_control_plane_busy503yesAnother mutation holds the company-family lease. Retry after the advertised delay.
company_owner_unavailable503yesThe owner record backing your session could not be read. Retry; if it persists, authenticate with a bf_ API key instead of an anonymous cookie.
idempotency_key_conflict409noThis Idempotency-Key was already used with a DIFFERENT request body. Not retryable — either reuse the original body or pick a new key.
company_data_seed_conflict409noThe requested data_seed_id disagrees with the seed already bound to this company instance. Use the bound seed or a new company_instance_key.
release_fence_mismatch409noThe world moved to a newer release fence while this request was in flight. Re-read the world and reissue against the current fence.

curl

curl -sX POST https://blobfish.ai/api/v1/sandbox/jobs \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: campaign-request-001' \
  -d '{"prompt":"insurance carrier operations","company_instance_key":"northstar_mutual","target_failure_rate":0.5,"anchor_files":[{"filename":"CUSTOMER_PRD.md","content":"# Requirements"}],"fresh":true}'
GET/api/v1/sandbox/jobs/{jobId}

Read owner-gated job metadata as a point-in-time snapshot. For a long-running build, keep GET /jobs/{jobId}/stream open and reconnect as documented there; do not use snapshot polling as the execution transport. A ready job returns world_id; fetch the full world separately from GET /worlds/{worldId}. TERMINAL-STATE CONTRACT: every job ends in exactly one of "ready" or "failed". On failure, job.error names the stage and reason.

Response

{
  "job": {
    "job_id": "job_e78af19041f046eb",
    "status": "ready",
    "world_id": "sbx_9f3aa4bc6f684ff3",
    "stages": [
      {
        "key": "run_first_test",
        "label": "Running first test",
        "status": "completed"
      }
    ]
  }
}

curl

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

Retry an owner-gated failed build from its durable checkpoint. Omit stage to resume the exact failed table stage, or pass one of create_world, make_package, launch_sandbox, or run_first_test to restart that UI stage group. The operation is replay-safe: repeating it after the first request queued or completed the job returns the unchanged snapshot with retry_replayed=true. Checkpoints from incompatible deployed compiler contracts return HTTP 409.

Request Body

FieldTypeRequiredDescription
actionstringNoOnly "retry" is accepted; omitted defaults to retry
stagestringNoOptional UI stage group; omit to retry the failed stage

Response

{
  "job": {
    "job_id": "job_e78af19041f046eb",
    "status": "running_first_test"
  },
  "queue": "job_table",
  "retry_replayed": false
}

Errors

codestatusretryablemeaning
execution_scope_mismatch409noThe durable checkpoint belongs to another deployed compiler scope. Submit a new idempotent generation or revision request against the active API contract.
release_fence_mismatch409noThe job is fenced to an incompatible active Sandbox contract. Submit a new idempotent generation or revision request.
release_fence_invalid409noThe job's persisted release fence is malformed and cannot be executed safely.

curl

curl -sX POST https://blobfish.ai/api/v1/sandbox/jobs/job_e78af19041f046eb \
  -H 'Content-Type: application/json' \
  -d '{"action":"retry"}'
GET/api/v1/sandbox/jobs/{jobId}/stream

Primary progress transport for a long-running build. Keep this owner-gated SSE request open so serverless execution remains active, and reconnect after a timeout or transport loss. Emits stage transitions, pipeline progress, 15-second heartbeat liveness receipts, thesis previews, and a terminal done/error event. GET /jobs/{jobId} is the point-in-time snapshot endpoint.

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":"heartbeat","job_id":"job_e78af19041f046eb","status":"creating_world","updated_at":"2026-08-04T14:00:54.241Z"}

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 30 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 a declarative preview world. JSON imports a SandboxWorld-shaped payload; multipart imports a non-executable Blobfish download package into the hosted preview store. Caller-controlled Python tool source and verifier VCode are refused here because the website process is not a code sandbox. Use POST /api/v1/worlds/import for canonical executable worlds.

Request Body

FieldTypeRequiredDescription
tablesarrayYes[{name, columns:[{name,type,pk?,fk?}], sample_rows:[...]}] -- rows seed the live SQLite (JSON mode)
toolsarrayYes[{name, type, parameters, target_tables}] -- declarative preview metadata only; executable source is refused (JSON mode)
tasksarrayNo[{task_id, prompt, required_tools, ...}] -- extra provenance fields (expected_calls) ride through (JSON mode)
verifiersarrayNoNon-executable preview metadata only; verifier VCode belongs on the canonical /api/v1/worlds/import path
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": "/w/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
POST/api/v1/worlds/import

Import a canonical executable world into the private world-factory runtime. The tar.gz is admitted without executing caller code, bound to the authenticated API key, and later runs only through the fleet sandbox. Exact nested tool schemas, SQLite state, scenarios, tasks, and VCode verifiers survive unchanged. The response returns an owner-gated HTTP MCP URL and task-evaluation template.

Request Body

FieldTypeRequiredDescription
worldfileYesCanonical world tar.gz containing env_spec.json, environment.db, tools.json, and tasks.json
world_idstringNoOptional stable runtime id; conflicts fail closed

Response

{
  "world_id": "env_nario_mock",
  "status": "ready",
  "created": true,
  "archive_sha256": "4d3c…",
  "validation": {
    "sqlite_integrity": "ok",
    "scenario_count": 8,
    "verifier_count": 42
  },
  "tool_count": 117,
  "task_count": 42,
  "mcp_url": "/api/v1/worlds/env_nario_mock/mcp",
  "evaluate_url_template": "/api/v1/worlds/env_nario_mock/tasks/{task_id}/evaluate",
  "runtime": "fleet_sandbox",
  "ownership": "api_key"
}

curl

# Fast path from an app-mirror skill bundle:
blobfish mirror publish ./worlds/env_nario_mock \
  --base https://blobfish.ai --api-key bf_YOUR_KEY --out ./nario-world.tar.gz

# Equivalent raw API call:
curl -sX POST https://blobfish.ai/api/v1/worlds/import \
  -H 'X-API-Key: bf_YOUR_KEY' \
  -F world=@nario-world.tar.gz -F label="Nario exact mock"
POST/api/v1/worlds/{worldId}/mcp

Owner-gated Streamable HTTP MCP for an imported executable world. Call initialize without a session id, then reuse the returned Mcp-Session-Id for tools/list, tools/call, task tools, scenario switching, reset, and DELETE cleanup. Every session forks isolated SQLite state.

Request Body

FieldTypeRequiredDescription
jsonrpcstringYesAlways '2.0'
methodstringYesinitialize, tools/list, tools/call, resources/list, resources/read, or ping
paramsobjectNoMCP method parameters

Response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "serverInfo": {
      "name": "fleet-world-factory-mcp",
      "version": "1.0.0"
    }
  }
}

curl

curl -i -sX POST https://blobfish.ai/api/v1/worlds/env_nario_mock/mcp \
  -H 'X-API-Key: bf_YOUR_KEY' -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}'

# Reuse the Mcp-Session-Id response header:
curl -sX POST https://blobfish.ai/api/v1/worlds/env_nario_mock/mcp \
  -H 'X-API-Key: bf_YOUR_KEY' -H 'Mcp-Session-Id: SESSION_ID' \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
POST/api/v1/worlds/{worldId}/tasks/{taskId}/evaluate

Run one imported TaskSpec through the deterministic sandbox agent and its executable verifier. Returns the trajectory, assertion detail, binary pass/fail, and reward without exposing verifier source to the agent.

Response

{
  "world_id": "env_nario_mock",
  "task_id": "task_invite_member",
  "passed": true,
  "reward": 1,
  "steps": [
    {
      "step": 1,
      "tool": "inviteMember",
      "arguments": {
        "email": "new@example.com"
      }
    }
  ],
  "verifier_detail": {
    "kind": "exact_call_trace",
    "scored": true
  }
}

curl

curl -sX POST https://blobfish.ai/api/v1/worlds/env_nario_mock/tasks/task_invite_member/evaluate \
  -H 'X-API-Key: bf_YOUR_KEY'

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 andMcp-Session-Id on every call so repeated or parallel runs do not share state. The legacy X-Blobfish-Session alias remains accepted. 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'
# → Mcp-Session-Id: sess_...
# → { "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 'Mcp-Session-Id: 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