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.
https://blobfish.aiEnvironments — 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_9f2c4b18aaSwap 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 scoresGetting 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).
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/keyscurl -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...'Generate a World
Send a domain prompt and get back a full executable world with tables, tools, tasks, and verifiers.
POST /api/v1/sandbox/jobscurl -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" }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_..."}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[].vcodeRun 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-taskcurl -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": [...] } }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}/qualitycurl -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": {...}}] } }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}/downloadcurl -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 oracleDeploy & 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.
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 clonegit clone git@github.com:blobfishai/blobfishai.git ~/workplace/blobfishaiInstall 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.shbash ~/workplace/blobfishai/skills/blobfish/install.sh /path/to/your-repo
# → installed blobfish skill → /path/to/your-repo/.claude/skills/blobfishHealth check
Verifies Python, the checkout, and every pipeline import. Run this first whenever something misbehaves.
blobfish doctorbash /path/to/your-repo/.claude/skills/blobfish/scripts/blobfish doctor
# python: 3.12.x ✓ · blobfish home ✓ · imports ✓ · doctor: OKGenerate 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 generatecd /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/heldoutConnect 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 addclaude 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-day1Practice, 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 · reportBF=.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-skillHosted 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.
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"}'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_…", ... }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}/sessionscurl -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" }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}/mcpcurl -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":{}}'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}/resetcurl -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.
/api/v1/sandbox/generateSynchronous 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
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Domain description, e.g. "law firm matter intake" |
anchor_files | array | Yes | Uploaded anchor documents with filename/content, e.g. PRD markdown or OpenAPI JSON |
stream | boolean | No | If 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"}]}'/api/v1/sandbox/jobsAsync 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
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes | Domain description for world generation |
company_instance_key | string | No | Stable 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_rate | number | No | Desired baseline failure rate, e.g. 0.5 for maximum training signal |
requested_task_count | number | No | Advanced 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_files | array | No | Inline uploaded files as {filename, content, content_type?}: PRDs, API specs, schemas, policies, traces, or source samples |
mcp_apps | array | No | MCP/Composio app selections with optional tool schemas |
mock_services | array | No | Production-service twins to compile, each with service, optional data_seed_id, and tool_limit |
fresh | boolean | No | Build 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
| code | status | retryable | meaning |
|---|---|---|---|
company_control_plane_unavailable | 503 | yes | The 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_busy | 503 | yes | Another mutation holds the company-family lease. Retry after the advertised delay. |
company_owner_unavailable | 503 | yes | The 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_conflict | 409 | no | This 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_conflict | 409 | no | The 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_mismatch | 409 | no | The 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}'/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/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
| Field | Type | Required | Description |
|---|---|---|---|
action | string | No | Only "retry" is accepted; omitted defaults to retry |
stage | string | No | Optional 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
| code | status | retryable | meaning |
|---|---|---|---|
execution_scope_mismatch | 409 | no | The durable checkpoint belongs to another deployed compiler scope. Submit a new idempotent generation or revision request against the active API contract. |
release_fence_mismatch | 409 | no | The job is fenced to an incompatible active Sandbox contract. Submit a new idempotent generation or revision request. |
release_fence_invalid | 409 | no | The 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"}'/api/v1/sandbox/jobs/{jobId}/streamPrimary 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/api/v1/sandbox/jobsList 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/api/v1/sandbox/worlds/importImport 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
| Field | Type | Required | Description |
|---|---|---|---|
tables | array | Yes | [{name, columns:[{name,type,pk?,fk?}], sample_rows:[...]}] -- rows seed the live SQLite (JSON mode) |
tools | array | Yes | [{name, type, parameters, target_tables}] -- declarative preview metadata only; executable source is refused (JSON mode) |
tasks | array | No | [{task_id, prompt, required_tools, ...}] -- extra provenance fields (expected_calls) ride through (JSON mode) |
verifiers | array | No | Non-executable preview metadata only; verifier VCode belongs on the canonical /api/v1/worlds/import path |
thesis | object | No | {company, domain, vertical, ...} shown in the workspace header (JSON mode) |
world | file | No | tar.gz archive of the mirror world directory (multipart mode) |
label | string | No | Human-readable label for the imported world (multipart mode) |
Query Parameters
| Param | Type | Required | Description |
|---|---|---|---|
target | string | No | '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/api/v1/worlds/importImport 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
| Field | Type | Required | Description |
|---|---|---|---|
world | file | Yes | Canonical world tar.gz containing env_spec.json, environment.db, tools.json, and tasks.json |
world_id | string | No | Optional 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"/api/v1/worlds/{worldId}/mcpOwner-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
| Field | Type | Required | Description |
|---|---|---|---|
jsonrpc | string | Yes | Always '2.0' |
method | string | Yes | initialize, tools/list, tools/call, resources/list, resources/read, or ping |
params | object | No | MCP 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"}'/api/v1/worlds/{worldId}/tasks/{taskId}/evaluateRun 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_shimwith 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