Skip to content

API reference

The API runs the same pipelines as the CLI over HTTP, so a UI or other tooling can drive them. Each POST starts a pipeline as a background task and returns the task's ID at once. You then poll the task for its log and, when it finishes, its result.

Running the server

The API is an optional extra:

uv sync --extra api            # or: pip install 'developer[api]'
developer serve                # 127.0.0.1:6768; --host and --port to change

To serve beyond this machine, set DEVELOPER_API_TOKEN first. Every endpoint except the health check then needs it as a bearer token:

export DEVELOPER_API_TOKEN=$(openssl rand -hex 32)
developer serve --host 0.0.0.0

Without a token the server accepts any request, so developer serve refuses to start without one on anything but a loopback address. --allow-insecure overrides the refusal:

developer serve --host 0.0.0.0 --allow-insecure

That is reasonable when something else controls who can reach the port: a container whose port is published only on the host's loopback, a firewall or VPN, or a reverse proxy that handles login. Otherwise anyone who can reach the port can run agents that push to your repos. A token that is set is still enforced, with or without the flag.

Example

auth="Authorization: Bearer $DEVELOPER_API_TOKEN"

curl -X POST localhost:6768/review -H "$auth" \
  -H 'Content-Type: application/json' -d '{"repo": "myrepo", "pr": 42}'
# {"task_id": "a1b2c3d4e5f6", "status": "pending"}

curl localhost:6768/tasks/a1b2c3d4e5f6 -H "$auth"
{
  "task_id": "a1b2c3d4e5f6",
  "status": "completed",
  "log": ["Reviewing PR #42", "Fetching PR #42 from gregology/myrepo...", "Review pipeline complete"],
  "result": {
    "pipeline": "review",
    "dry_run": false,
    "pr": 42,
    "comment_url": "https://github.com/gregology/myrepo/pull/42#issuecomment-123",
    "metrics": [{"stage": "analyze", "model": "claude-opus-5-5", "turn_count": 12, "cost_usd": 0.41}]
  },
  "error": null
}

(The log and metrics above are shortened.)

Endpoints

Method and path Body Does
GET / Health check, no token needed: {"status": "ok"}
GET /repos Lists the repos in developer.yaml: [{"name", "owner", "repo"}]
POST /repos {"owner", "repo"} Starts add: investigates the repo and writes its config. Returns 202
POST /resolve {"repo", "issue", "dry_run"} Starts resolve on an issue
POST /review {"repo", "pr", "dry_run"} Starts review on a PR
POST /address {"repo", "pr", "dry_run"} Starts address on a PR
POST /audit/{type} {"repo", "dry_run", "limit"} Starts an audit. type is docs, refactor or tests
POST /validate {"repo"} or {"validate_all": true} Starts validate on one repo or all of them
GET /tasks Lists every task, newest first
GET /tasks/{task_id} Shows one task
  • repo is a key in developer.yaml ("myrepo") or "owner/repo".
  • dry_run defaults to false. Like the CLI's --dry-run, the pipeline does the work but publishes nothing.
  • limit caps an audit's findings and defaults to 3.
  • Developer reads developer.yaml on every request, so you can edit it without restarting the server.

Every POST that starts a task returns {"task_id": "...", "status": "pending"}.

Tasks

Field Description
task_id The task's ID
status pending, running, completed or failed
log What Developer logged while the task ran, one line per entry
result The pipeline's result once the task completes, otherwise null
error Why the task failed, otherwise null

Only one active task may work on each target: an issue, a PR (review and address count as the same target), a kind of audit, or a repo being added or validated. A second request for that target gets a 409 until the first task finishes.

Tasks run in background threads and live in the server's memory. Restarting the server forgets them, and past 1000 tasks the oldest finished ones are dropped. Each run also leaves a record in runs/, the same as a CLI run (see Architecture).

Results

Every result has a pipeline field. The remaining fields depend on the pipeline:

pipeline Fields
resolve dry_run, issue, decision, pr_url, comment_url, metrics
review dry_run, pr, comment_url, metrics
address dry_run, pr, comment_url, metrics
audit dry_run, audit, findings, issue_urls, metrics
validate results (repo name to true/false), all_passed
add_repo repo (its key in developer.yaml), config_saved
  • decision is the triage decision: ask, propose, action_proposed, action_direct, decline or decision.
  • pr_url and comment_url link to the PR or comment the run published. They are null when it published none, as on a dry run. A resolve run publishes either a PR or a comment.
  • findings holds the audit's findings. Each one has a title, body and labels, and docs findings also have a verdict and confidence. issue_urls holds the issues filed for them, and is empty on a dry run.
  • metrics has one entry per stage, holding its stage, model, duration_seconds, turn_count, tool_call_count, token counts, cost_usd, model_usage and rate_limits. These are the same numbers as in the metrics block on what Developer posts.

Errors

Errors return {"detail": ...}.

Status When
400 Unknown audit type, or /validate without repo or validate_all
401 Missing or wrong bearer token, when the server has one
404 Unknown repo or task
409 The repo is already configured (POST /repos), or another task is working on the same target
422 The body is missing a field or has one of the wrong type

Interactive docs

While the server is running, FastAPI serves documentation generated from the request and response models at /docs (Swagger UI) and /redoc.