# Computalot API Reference (Beta)

Computalot is a distributed compute platform. Submit jobs, get structured JSON results. GPU and CPU workers.

**Private beta** — running jobs requires an admin-issued API key or admin-whitelisted wallet session. Discovery is public. We're actively building and want your feedback.

If you do not already have beta access, join the waitlist at `/` or ask an admin to provision access.

API keys are admin-issued during beta.

Base URL: `https://computalot.com`

- `https://computalot.com/skill.md` — install this skill to get started
- `https://computalot.com/llms.txt` — this compact reference
- `https://computalot.com/llms-full.txt` — full reference with tutorials
- `https://computalot.com/api/v1/docs` — machine-readable JSON index
- `https://computalot.com/docs` — human docs

## Feedback — Report Bugs & Request Features

This is beta software. Please report bugs, request features, and share ideas:

```bash
curl -sS -X POST https://computalot.com/api/v1/feedback \
  -H "Content-Type: application/json" \
  -d '{"type": "bug", "title": "Brief summary", "description": "What happened, what you expected"}'
```

Types: `bug`, `feature_request`, `provisioning`, `job_type_request`. No auth required.

## Two Paths

**Sealed Recipes** — Platform compute primitives. No project setup. Send typed payloads, get results.

**Projects** — Bring your own code. Create a project, push a tarball, submit jobs with `runner_command`.

## Auth

```bash
# API key (admin-issued during beta)
export TOKEN="flk_..."

# Wallet session (admin-whitelisted wallets)
# 1. POST /api/v1/auth/wallet/challenge
# 2. Sign challenge.message with your wallet
# 3. POST /api/v1/auth/wallet/verify → returns fls_... token

# All protected endpoints:
Authorization: Bearer $TOKEN
```

No auth required: `/health`, `/docs`, `/llms.txt`, `/llms-full.txt`, `/api/v1/docs/*`, `POST /api/v1/feedback`, `POST /api/v1/auth/wallet/challenge`, `POST /api/v1/auth/wallet/verify`.

`GET /metrics` is operator-gated: local requests, admin auth, or the dedicated metrics token only.

## Sealed Recipe Quickstart

```bash
# 1. List recipes
curl -sS -H "Authorization: Bearer $TOKEN" https://computalot.com/api/v1/recipes

# 2. Submit a recipe job
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  https://computalot.com/api/v1/recipes/packing/jobs \
  -d '{"payload": {"operation": "eval", "candidate": [0.12, 0.31, 0.88]}, "timeout_s": 300}'

# 3. Read results
curl -sS -H "Authorization: Bearer $TOKEN" https://computalot.com/api/v1/results/<job_id>
```

Recipe jobs don't need `type` or `runner_command` — the recipe provides both.

## Recipe Catalog

| Recipe | Operations | Key inputs |
|--------|-----------|------------|
| `prop_amm` | `eval`, `eval_chunk`, `validate`, `validate_full`, `bpf_eval`, `bpf_eval_chunk`, `build`, `concavity_check` | `strategy_ref` or `rs_source_b64`, `seed_base`, `count`, `steps` |
| `packing` | `eval`, `eval_batch`, `feasible_optimize`, `basin_hopping`, `differential_evolution` | `candidate` (45-dim array) or `candidate_ref`, `candidates` or `candidates_ref` |
| `lightgbm_train` | `train`, `cross_validate` | `dataset_ref`, `target_column`, LightGBM hyperparams |
| `echidna` | `foundry_prebuilt`, `hardhat_prebuilt` | `project_ref`, `contract`, `test_mode`, `test_limit` |

Inspect schema before submitting: `GET /api/v1/recipes/:name` returns `payload_schema`, `operations`, `artifact_inputs`.

## Project Quickstart

```bash
# 1. Create project
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  https://computalot.com/api/v1/projects \
  -d '{"name": "my-proj", "remote_dir": "/root/projects/my-proj"}'

# 2. Upload tarball (raw binary, NOT multipart)
tar czf code.tar.gz Dockerfile computalot.project.json script.py
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
  --data-binary @code.tar.gz \
  https://computalot.com/api/v1/projects/my-proj/push

# 3. Submit job immediately after push
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  https://computalot.com/api/v1/jobs \
  -d '{"type": "structured_runner", "runner_command": ["python3", "script.py"], "payload": {"test": true}, "project": "my-proj", "timeout_s": 120}'

# 4. Optional: prepare currently available workers ahead of time
curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  https://computalot.com/api/v1/projects/my-proj/init

# 5. Inspect published vs warm state
curl -sS -H "Authorization: Bearer $TOKEN" \
  https://computalot.com/api/v1/projects/my-proj/status

# 6. Results
curl -sS -H "Authorization: Bearer $TOKEN" https://computalot.com/api/v1/results/<job_id>
```

## Project Lifecycle & Readiness

- Readiness is **active revision** truth, not machine-count truth
- After `POST /api/v1/projects/:name/push`, a successful response can include `tarball_diff`
- Use `GET /api/v1/projects/:name/status` for top-level readiness and `GET /api/v1/projects/:name/status/details` for diagnostics + recovery guidance
- `can_accept_new_jobs: true` means the latest revision is published and can be submitted immediately
- `ready_for_jobs` can stay false after push while the first job or an optional `/init` prepares runtime

## Job Types

| Type | Use case | Key fields |
|------|----------|------------|
| `structured_runner` | Run script with JSON in/out, optional fan-out | `runner_command`, `payload`, `fan_out`, `merge_strategy` |
| `sweep` | Grid search over parameter combinations | `runner_command`, `parameters`, `fixed_payload`, `rank_by` |
| `map_reduce` | Chunked parallelism with reduce operators | `runner_command`, `split`, `reduce`, `payload` |
| `benchmark` | Compare named candidates with replicas | `runner_command`, `candidates`, `shared_payload`, `replicas`, `rank_by` |

Default to `structured_runner` unless another type clearly fits.

## Runner Protocol

Your script receives input and writes output via environment variables:

- `$COMPUTALOT_TASK_PAYLOAD` — path to JSON input file. Read this.
- `$COMPUTALOT_TASK_RESULT` — path to write JSON output. Computalot reads this after exit.
- `$COMPUTALOT_ARTIFACT_DIR` — directory for output files. Auto-uploaded on completion.
- Exit 0 = success. Non-zero = failure.
- Progress: print `COMPUTALOT_PROGRESS:{"epoch":5,"loss":0.23}` to stdout.

```python
import json, os
payload = json.load(open(os.environ['COMPUTALOT_TASK_PAYLOAD']))
result = {'score': 0.95, 'model': payload['model']}
json.dump(result, open(os.environ['COMPUTALOT_TASK_RESULT'], 'w'))
```

## Fan-Out

Split one job into parallel tasks:

**By list values** — one task per item:
```json
{"fan_out": {"by": "models"}, "payload": {"models": ["gpt4", "claude", "llama"]}}
```

**By explicit items** — custom payload per task:
```json
{"fan_out": {"items": [{"params": [0.1, 0.5]}, {"params": [0.2, 0.4]}]}}
```

**By chunks** — split a numeric range:
```json
{"fan_out": {"chunks": 20, "range_field": "total_seeds", "total": 10000}}
```

These are mutually exclusive. Add `batch_size` to group tiny items into one task. Merge strategies: `collect` (default), `keyed`, `weighted_avg`.

Choose exactly one fan-out shape per submit. Mixed shapes are rejected with `422`.

## Common Job Fields

| Field | Type | Default | Notes |
|-------|------|---------|-------|
| `project` | string | required | Must match a registered project |
| `timeout_s` | int | 3600 | Per-task timeout |
| `max_retries` | int | 0 | Auto-retry failed tasks |
| `priority` | string | `normal` | `high`, `normal`, or `low` |
| `depends_on` | [string] | [] | Job IDs that must complete first |
| `tags` | [string] | [] | Labels for grouping/filtering (max 20) |
| `callback_url` | string | null | Webhook URL for terminal status |
| `requirements` | object | null | `{cpu, memory_mb, gpu_count, gpu_memory_mb, profile, storage_gb}` |
| `checkpointing` | object | null | `{enabled, resume_from_latest}` |

Long ML jobs: use `_artifacts.download` for one-off large inputs, manifest `data_sources` for immutable remote weights/datasets, and manifest `cache_mounts` for writable runtime caches. `hf-mount` only applies to manifest-declared Hugging Face `data_sources`, not arbitrary runner-side downloads.

## Results & Streaming

```bash
# Terminal results
curl -sS -H "Authorization: Bearer $TOKEN" https://computalot.com/api/v1/results/<job_id>

# Per-task details and progress
curl -sS -H "Authorization: Bearer $TOKEN" https://computalot.com/api/v1/jobs/<job_id>/tasks

# Stdout/stderr
curl -sS -H "Authorization: Bearer $TOKEN" https://computalot.com/api/v1/jobs/<job_id>/output

# SSE stream (one job)
curl -sS -N -H "Authorization: Bearer $TOKEN" https://computalot.com/api/v1/jobs/<job_id>/stream

# SSE stream (multiple jobs)
curl -sS -N -H "Authorization: Bearer $TOKEN" "https://computalot.com/api/v1/jobs/watch?ids=<id1>,<id2>"
```

Job lifecycle: `planning` → `queued` → `running` → `completed` | `partial` | `failed` | `cancelled`

Debug failures: `GET /api/v1/jobs/:id` for `error` and `recommended_action`, `GET /api/v1/jobs/:id/tasks` for per-task diagnostics.

During retries, `GET /api/v1/jobs/:id/tasks` and `GET /api/v1/jobs/:id/output` preserve the most recent failed attempt's diagnostics until the current attempt emits its own output.

Public task/result payloads keep the submitted task contract visible, but redact provider IDs, raw runtime paths, and image refs/digests.

## Billing

- Check balance: `GET /api/v1/account/balance`
- Jobs reserve a hold on submission, settle to actual usage on completion
- Project init is free but requires $5 available balance
- Fund via x402: `POST /api/v1/account/quotes/topup` → pay → `POST /api/v1/account/quotes/:id/pay/x402`
- If a request returns `402`, fund the account and retry the same request

Billing truth lives on `GET /api/v1/account/balance`, `GET /api/v1/account/holds`, and `GET /api/v1/account/ledger`.

Use `GET /api/v1/account/quotes` to inspect open top-up and shortfall quotes before retrying blocked work.

If project init or job submit returns a shortfall quote, fund the account and retry `POST /api/v1/projects/:name/init` or retry the same submit request to `POST /api/v1/jobs`.

## Python SDK & CLI

```bash
python3 -m pip install --user --break-system-packages \
  https://computalot.com/docs/downloads/computalot-0.2.0-py3-none-any.whl
export PATH="$HOME/.local/bin:$PATH"
```

```python
from computalot import ComputalotClient
client = ComputalotClient(controller_url="https://computalot.com", token="YOUR_TOKEN")
recipes = client.list_recipes()
jobs = client.list_jobs(limit=5)
print(len(recipes.get("recipes", [])))
print(len(jobs.get("jobs", [])))
```

```bash
computalot docs --llm
computalot jobs --limit 5
computalot job <job_id>
```

## Endpoint Reference

### Recipes
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/v1/recipes` | List all recipes |
| GET | `/api/v1/recipes/:name` | Recipe schema and operations |
| POST | `/api/v1/recipes/:name/jobs` | Submit a recipe job |

### Jobs
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/v1/jobs` | Submit job |
| POST | `/api/v1/jobs/batch` | Submit up to 200 jobs |
| GET | `/api/v1/jobs?status=&project=&tag=&limit=50` | List jobs |
| GET | `/api/v1/jobs/:id` | Job state |
| GET | `/api/v1/jobs/:id/output` | Stdout/stderr |
| GET | `/api/v1/jobs/:id/tasks` | Per-task details and progress |
| GET | `/api/v1/jobs/:id/events?limit=200` | Lifecycle events |
| GET | `/api/v1/jobs/:id/stream` | SSE stream (one job) |
| GET | `/api/v1/jobs/watch?ids=a,b,c` | SSE stream (multiple jobs, max 100) |
| PUT | `/api/v1/jobs/:id/cancel` | Cancel job |
| GET | `/api/v1/presets` | Resource presets (use to populate requirements) |

### Billing
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/v1/account/balance` | Balance and holds |
| GET | `/api/v1/account/ledger` | Transaction history |
| GET | `/api/v1/account/holds` | Active holds |
| GET | `/api/v1/account/quotes` | Funding quotes |
| POST | `/api/v1/account/quotes/topup` | Create top-up quote |
| POST | `/api/v1/account/quotes/:id/pay/x402` | Settle x402 quote |

### Results & Artifacts
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/api/v1/results/:job_id` | Per-task results |
| POST | `/api/v1/artifacts` | Upload artifact (max 2GB) |
| POST | `/api/v1/artifacts/direct` | Direct upload URL |
| POST | `/api/v1/artifacts/multipart` | Start resumable upload |
| POST | `/api/v1/artifacts/multipart/complete` | Finalize upload |
| GET | `/api/v1/artifacts` | List artifacts |
| GET | `/api/v1/artifacts/:id` | Download artifact |

### Projects
| Method | Path | Purpose |
|--------|------|---------|
| POST | `/api/v1/projects` | Create project |
| GET | `/api/v1/projects` | List projects |
| GET | `/api/v1/projects/:name` | Project config |
| PUT | `/api/v1/projects/:name` | Update metadata |
| POST | `/api/v1/projects/:name/push` | Upload tarball |
| DELETE | `/api/v1/projects/:name` | Delete project |
| POST | `/api/v1/projects/:name/init` | Start setup |
| GET | `/api/v1/projects/:name/status` | Check readiness |
| GET | `/api/v1/projects/:name/status/details` | Setup diagnostics |
| POST | `/api/v1/projects/:name/invalidate` | Reset for re-init |
| PUT | `/api/v1/projects/:name/kv/:key` | Write shared state |
| GET | `/api/v1/projects/:name/kv/:key` | Read shared state |
| GET | `/api/v1/projects/:name/stream` | SSE stream for project |

### Public (no auth)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/skill.md` | Agent skill file — start here |
| GET | `/llms.txt` | This compact reference |
| GET | `/llms-full.txt` | Full reference with tutorials |
| GET | `/api/v1/docs` | JSON docs index |
| GET | `/api/v1/docs/recipes` | Recipe guide |
| GET | `/api/v1/docs/python-sdk` | Python SDK guide |
| GET | `/api/v1/docs/workflows` | Workflow patterns |
| POST | `/api/v1/auth/wallet/challenge` | Start wallet auth |
| POST | `/api/v1/auth/wallet/verify` | Complete wallet auth |
| POST | `/api/v1/feedback` | Report bugs and request features |

### Ops (operator-facing)
| Method | Path | Purpose |
|--------|------|---------|
| GET | `/health` | Liveness probe (no auth) |
| GET | `/live` | Liveness probe (no auth, same as `/health`) |
| GET | `/ready` | Readiness probe (no auth; `503` until controller core is up) |
| GET | `/metrics` | Prometheus metrics (admin auth, dedicated metrics token, or local request) |
