Documentation

Connect Claude Code, Cursor, or any agent to Foundry notebooks over the Agent API. Create notebooks, upload data, train on Modal GPUs, and poll live logs.

Base URL: https://foundry-mocha-ten.vercel.app

Getting started

  1. Create an account and verify your email.
  2. Open Settings → API keys and create a key. Copy the secret once — it starts with fdry_sk_.
  3. Give that key to your AI agent (Claude Code, Cursor, custom script) as FOUNDRY_API_KEY.
  4. Point the agent at https://foundry-mocha-ten.vercel.app/api/v1.

For AI agents

Foundry is built so coding agents can drive training without opening the UI. Tell the agent:

  • Host: https://foundry-mocha-ten.vercel.app
  • Auth header: Authorization: Bearer fdry_sk_…
  • JSON for most calls; multipart form for file uploads (files=@path).
  • Preferred loop: create notebook → upload dataset → POST …/train → poll …/status and …/logs.

Paste this into Claude Code

You have access to Foundry Agent API.

Base URL: https://foundry-mocha-ten.vercel.app
Auth: Authorization: Bearer $FOUNDRY_API_KEY
(Key looks like fdry_sk_… — never print the full secret.)

Do this for training jobs:
1. POST /api/v1/notebooks  { "title":"…", "templateId":"llm-lora", "gpu":"T4" }
2. POST /api/v1/notebooks/{id}/files  multipart field "files" with dataset
3. PATCH /api/v1/notebooks/{id}  optional trainConfig (epochs, datasetNote, …)
4. POST /api/v1/notebooks/{id}/train  {}
5. Poll GET /api/v1/notebooks/{id}/status and /logs until status is ready or failed

Templates: llm-lora | whisper | handwriting | video | voice | robotics
GPUs: cpu | T4 | L4 | A10G

If you are Claude in Anthropic's cloud sandbox: you CANNOT curl this host
(egress allowlist). Use the GitHub Actions bridge instead — see docs #claude-cloud.
Cursor / local Claude Code can call Foundry directly.

Claude Cloud sandbox

Anthropic's cloud agent runs behind an egress proxy with a fixed hostname allowlist (GitHub, npm, PyPI, crates.io, etc.). foundry-mocha-ten.vercel.app is not on that list, so the proxy returns host_not_allowedbefore the request ever reaches Foundry. We cannot change Anthropic's allowlist from Foundry.

What works

  • Cursor (or Claude Code on your machine) — uses your PC network; call /api/v1 directly with your fdry_sk_ key.
  • GitHub Actions bridge — Claude talks to api.github.com(allowlisted). A workflow on GitHub's runners calls Foundry for you.

Set up the bridge (once)

  1. Copy templates/github-agent-bridge/foundry-agent-bridge.yml into your repo as .github/workflows/foundry-agent-bridge.yml.
  2. Repo → Settings → Secrets and variables → Actions → add FOUNDRY_API_KEY = your fdry_sk_… secret. Optional: FOUNDRY_HOST = https://foundry-mocha-ten.vercel.app.
  3. Tell cloud Claude to dispatch the workflow instead of curling Foundry.

Paste this into Claude Cloud

You cannot reach Foundry's Vercel host from this sandbox (egress allowlist).

Use the GitHub Actions bridge instead:

gh workflow run foundry-agent-bridge.yml \
  -f action=create \
  -f title="From Claude Cloud" \
  -f template_id=llm-lora \
  -f gpu=T4

# After it finishes, read the artifact or:
gh run list --workflow=foundry-agent-bridge.yml --limit 1
gh run view RUN_ID --log

Other actions: list | train | status | logs | upload_note
(train/status/logs need -f notebook_id=…)

The user's dashboard at https://foundry-mocha-ten.vercel.app/dashboard/notebooks
will show notebooks created this way (same API key / account).

Asking Anthropic to allowlist your domain is possible as a product request, but it is not something Foundry or your API key can unlock today.

Authentication

Every /api/v1/* request needs a Bearer token. Keys are hashed at rest; the plaintext secret is shown once at creation.

curl "https://foundry-mocha-ten.vercel.app/api/v1/notebooks" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY"
  • Prefix: fdry_sk_
  • Max 5 active keys per account
  • Manage keys in Dashboard → Settings
  • Revoke or delete a key immediately if it leaks

Scopes

Keys can be limited to specific actions. New keys default to all scopes.

ScopeAllows
notebooks:readList notebooks, get status, read logs
notebooks:writeCreate, update, delete notebooks
notebooks:filesUpload / list / delete dataset files
notebooks:runRun cells (single or all)
notebooks:trainStart the train recipe on Modal

Missing scope → 403 with code: "forbidden". Bad or missing key → 401 unauthorized.

End-to-end flow

export FOUNDRY_API_KEY=fdry_sk_…
export HOST=https://foundry-mocha-ten.vercel.app

# 1) Create
curl -s -X POST "$HOST/api/v1/notebooks" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Agent train","templateId":"llm-lora","gpu":"T4"}'

# 2) Upload dataset (JSONL recommended)
curl -s -X POST "$HOST/api/v1/notebooks/NOTEBOOK_ID/files" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY" \
  -F "files=@./train.jsonl"

# 3) Optional config tweak
curl -s -X PATCH "$HOST/api/v1/notebooks/NOTEBOOK_ID" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"trainConfig":{"epochs":3,"datasetNote":"Support chat pairs"}}'

# 4) Train on Modal
curl -s -X POST "$HOST/api/v1/notebooks/NOTEBOOK_ID/train" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

# 5) Poll
curl -s "$HOST/api/v1/notebooks/NOTEBOOK_ID/status" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY"
curl -s "$HOST/api/v1/notebooks/NOTEBOOK_ID/logs" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY"

Endpoints

All paths are under /api/v1. Responses are JSON unless noted.

GET/api/v1/notebooks

List your notebooks and remaining credits.

Scope: notebooks:read

curl "$HOST/api/v1/notebooks" -H "Authorization: Bearer $FOUNDRY_API_KEY"
POST/api/v1/notebooks

Create a notebook. Optional: title, description, gpu, templateId, trainConfig, cells.

Scope: notebooks:write

curl -X POST "$HOST/api/v1/notebooks" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"My LoRA","templateId":"llm-lora","gpu":"T4"}'
GET/api/v1/notebooks/:id

Fetch full notebook: cells, trainConfig, events.

Scope: notebooks:read

PATCH/api/v1/notebooks/:id

Update title, description, gpu, cells, or trainConfig. Updating trainConfig refreshes the FOUNDRY_CONFIG cell.

Scope: notebooks:write

DELETE/api/v1/notebooks/:id

Delete a notebook.

Scope: notebooks:write

POST/api/v1/notebooks/:id/files

Upload dataset files. Multipart form field name: files (repeat for multiple). Max ~200 MB each.

Scope: notebooks:files

curl -X POST "$HOST/api/v1/notebooks/ID/files" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY" \
  -F "files=@./train.jsonl"
GET/api/v1/notebooks/:id/files

List uploaded files and workspace path.

Scope: notebooks:files

DELETE/api/v1/notebooks/:id/files

Remove a file. Body: { "name": "train.jsonl" }.

Scope: notebooks:files

POST/api/v1/notebooks/:id/train

Apply optional config, then run the full recipe on Modal (config → load → train → eval → export).

Scope: notebooks:train

curl -X POST "$HOST/api/v1/notebooks/ID/train" \
  -H "Authorization: Bearer $FOUNDRY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
POST/api/v1/notebooks/:id/run

Run cells. Body actions: { "action":"run_all" } | { "action":"run_cell","cellId":"…" } | { "action":"add_cell","kind":"code" }.

Scope: notebooks:run

GET/api/v1/notebooks/:id/status

Lightweight poll: status, running, failed, lastRunAt, Modal readiness.

Scope: notebooks:read

GET/api/v1/notebooks/:id/logs

Live events + cell outputs/errors. Optional ?since=ISO8601 to fetch only newer lines.

Scope: notebooks:read

Templates

Pass templateId on create:

  • llm-loraChat / instruction LoRA
  • whisperSpeech recognition
  • handwritingHandwriting / vision
  • videoShort video adapters
  • voiceVoice clone
  • roboticsImitation learning

Errors

{
  "error": "Invalid or missing API key",
  "code": "unauthorized"
}
  • 401 unauthorized — missing/invalid Bearer key
  • 403 forbidden — key lacks required scope
  • 404 — notebook not found for this account
  • 400 — validation / Modal not configured / run failure message in error

Dataset format

Prefer JSONL: one example per line. Short, clean pairs beat giant dumps.

{"messages":[
  {"role":"user","content":"Do you deliver Sundays?"},
  {"role":"assistant","content":"Yes — Nairobi only, 10am–4pm."}
]}
{"messages":[
  {"role":"user","content":"Shipping time to Mombasa?"},
  {"role":"assistant","content":"2–3 business days with standard courier."}
]}

After upload, open the notebook in the dashboard or keep driving it via the Agent API.

Credits

Cell runs and training spend credits (notebooks can be set free for testing via NOTEBOOKS_FREE). See Pricing and Billing.

Ready to connect an agent?

Create a key, paste the host + Bearer token into Claude Code or Cursor, and let it create notebooks for you.