HTTP API
Base URL: the address in listen, e.g. http://127.0.0.1:8080. Requests and responses are JSON; timestamps are Unix seconds; run IDs are 32 hex characters.
Authentication
Send the API token as a bearer token:
-H "Authorization: Bearer $SHIPYARD_API_TOKEN"
Queuing, cancelling and retrying always need it. When runtime.api_token_env is set, reads need it too. /healthz and /readyz are always open.
Queue a run
curl -X POST http://127.0.0.1:8080/api/v1/runs \
-H "Authorization: Bearer $SHIPYARD_API_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: nightly-check-2026-10-03' \
-d '{"project_id":"my-app","kind":"pipeline","task":"project-check","input":""}'
| Field | Meaning |
|---|---|
project_id |
An enabled project. |
kind |
diagnostic, pipeline or ai_text. |
task |
Pipeline name for pipeline; AI role for ai_text. |
input |
Pipeline input or the prompt. Required for ai_text. |
Idempotency-Key (8–128 characters) is required. Sending the same key and body again returns the run already queued instead of a duplicate; the same key with a different body is 409. Answer: 202 with {"data": run}.
Runs
| Request | Returns |
|---|---|
GET /api/v1/runs?limit=50 |
Newest runs first, up to 100 per page, and next_cursor. |
GET /api/v1/runs?before=<cursor> |
The next page; next_cursor is null on the last one. |
GET /api/v1/runs/{id} |
One run. |
POST /api/v1/runs/{id}/cancel |
Cancels a queued run, asks a running one to stop. |
POST /api/v1/runs/{id}/retry |
Queues a failed or cancelled run again as a new run. Needs a new Idempotency-Key. |
A run:
{
"id": "4bb3a90cb8f0a3bbc59125a2d3a992f8",
"project_id": "my-app",
"kind": "pipeline",
"task": "project-check",
"state": "succeeded",
"attempt": 1,
"created_at": 1791057353,
"updated_at": 1791057353,
"finished_at": 1791057353,
"logs_expired": false
}
state is one of queued, running, cancelling, succeeded, failed, cancelled. A failed run carries error.
Logs
curl -H "Authorization: Bearer $SHIPYARD_API_TOKEN" \
"http://127.0.0.1:8080/api/v1/runs/$ID/logs?offset=0"
Returns up to 100 events and next_offset; pass it back as offset to read on. attempt=N reads an earlier attempt. Expired logs answer 410.
{"data": [{"time": "2026-10-03T18:55:53Z", "level": "info", "run_id": "4bb3…", "attempt": 1, "message": "run started"}], "next_offset": 112}
Projects and pipelines
GET /api/v1/projects and GET /api/v1/pipelines return the configured projects and pipelines; prompts are never returned. read_only in the projects answer is true when the configuration came from a URL.
POST /api/v1/projects adds a project and writes it to the configuration file:
curl -X POST http://127.0.0.1:8080/api/v1/projects \
-H "Authorization: Bearer $SHIPYARD_API_TOKEN" \
-d '{"id":"firmware","repository":"you/firmware","enabled":true,"issue_label":"agent-ready",
"release_interval":"48h","runs_on":["arch=arm64"],
"content":{"enabled":false,"interval":"168h","min_score":70}}'
409 project_exists for a duplicate ID, 409 config_read_only for a URL-loaded configuration, 400 invalid_project with a message naming the problem.
Ships
| Request | Returns |
|---|---|
GET /api/v1/ships |
Ships with labels, platform, online and revoked; enabled tells whether the fleet endpoint is on. |
POST /api/v1/ships/tokens |
A one-time join token and the ship join command. Body: {"name": "garage-pi", "labels": ["arch=arm64"]}. |
POST /api/v1/ships/{id}/revoke |
Revokes a ship. |
Ships themselves talk to Shipyard over gRPC with mutual TLS — see Ships and docks.
Errors
Errors have a stable code:
{"error": {"code": "run_not_found"}}
| Status | Codes |
|---|---|
| 400 | invalid_input, invalid_project, invalid_name, invalid_label, invalid_cursor, invalid_attempt, invalid_offset, project_unavailable, pipeline_unavailable, model_or_input_unavailable, unsupported_kind, idempotency_key_required |
| 401 | unauthenticated |
| 404 | run_not_found, ship_not_found |
| 409 | idempotency_conflict, run_state_conflict, project_exists, config_read_only, fleet_disabled |
| 410 | logs_expired |
| 500 | storage_failed, enqueue_failed, logs_unavailable |
| 503 | storage_unavailable |
| 507 | log_budget_exceeded |
Metrics and the Alertmanager webhook are described in Monitoring.