Extracts the REST API section from README.md into a dedicated docs/api.md. Adds documentation for the four Jobs endpoints (GET/PUT /api/jobs, GET /api/jobs/:id, POST /api/jobs/:id/run) which existed in code but were not yet documented. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.8 KiB
Catalyst API Reference
All endpoints are under /api. Request and response bodies are JSON.
Instances
GET /api/instances
Returns all instances sorted by name. All query parameters are optional.
| Parameter | Type | Description |
|---|---|---|
search |
string | Partial match on name, vmid, or stack |
state |
string | Exact match: deployed, testing, degraded |
stack |
string | Exact match: production, development |
GET /api/instances?search=plex&state=deployed
[
{
"vmid": 117,
"name": "plex",
"state": "deployed",
"stack": "production",
"tailscale_ip": "100.64.0.1",
"atlas": 1, "argus": 1, "semaphore": 0,
"patchmon": 1, "tailscale": 1, "andromeda": 0,
"hardware_acceleration": 1,
"created_at": "2024-01-15T10:30:00",
"updated_at": "2024-03-10T14:22:00"
}
]
GET /api/instances/stacks
Returns a sorted array of distinct stack names present in the registry.
GET /api/instances/stacks
→ ["development", "production"]
GET /api/instances/:vmid
Returns a single instance by VMID.
| Status | Condition |
|---|---|
200 |
Instance found |
400 |
VMID is not a valid integer |
404 |
No instance with that VMID |
GET /api/instances/:vmid/history
Returns the audit log for an instance — newest events first.
| Status | Condition |
|---|---|
200 |
History returned (may be empty array) |
400 |
VMID is not a valid integer |
404 |
No instance with that VMID |
[
{
"id": 3,
"vmid": 117,
"field": "state",
"old_value": "testing",
"new_value": "deployed",
"changed_at": "2024-03-10T14:22:00"
},
{
"id": 1,
"vmid": 117,
"field": "created",
"old_value": null,
"new_value": null,
"changed_at": "2024-01-15T10:30:00"
}
]
POST /api/instances
Creates a new instance. Returns the created record.
| Status | Condition |
|---|---|
201 |
Created successfully |
400 |
Validation error — see errors array in response |
409 |
VMID already exists |
Request body:
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | |
vmid |
integer | yes | Must be > 0 and unique |
state |
string | yes | deployed, testing, or degraded |
stack |
string | yes | production or development |
tailscale_ip |
string | no | Valid IPv4 or empty string |
atlas |
0|1 | no | |
argus |
0|1 | no | |
semaphore |
0|1 | no | |
patchmon |
0|1 | no | |
tailscale |
0|1 | no | |
andromeda |
0|1 | no | |
hardware_acceleration |
0|1 | no |
PUT /api/instances/:vmid
Replaces all fields on an existing instance. Accepts the same body shape as POST. The vmid in the body may differ from the URL — this is how you change a VMID.
| Status | Condition |
|---|---|
200 |
Updated successfully |
400 |
Validation error |
404 |
No instance with that VMID |
409 |
New VMID conflicts with an existing instance |
DELETE /api/instances/:vmid
Deletes an instance. Only instances on the development stack may be deleted.
| Status | Condition |
|---|---|
204 |
Deleted successfully |
400 |
VMID is not a valid integer |
404 |
No instance with that VMID |
422 |
Instance is on the production stack |
Jobs
Jobs are background sync tasks that keep instance data up to date by pulling from external services (Tailscale, Patchmon, Semaphore). Each job runs on a configurable interval and can also be triggered on demand.
Sensitive config fields (api_key, api_token) are always returned as **REDACTED**. Send **REDACTED** back in a PUT to preserve the stored value unchanged.
GET /api/jobs
Returns all jobs.
[
{
"id": 1,
"key": "tailscale_sync",
"name": "Tailscale Sync",
"description": "Syncs Tailscale device status and IPs",
"enabled": 1,
"schedule": 15,
"config": { "api_key": "**REDACTED**", "tailnet": "example.com" },
"last_run_at": "2024-03-10T14:22:00",
"last_status": "success"
}
]
GET /api/jobs/:id
Returns a single job and its last 10 run records, newest first.
| Status | Condition |
|---|---|
200 |
Job found |
400 |
ID is not a valid integer |
404 |
No job with that ID |
{
"id": 1,
"key": "tailscale_sync",
"name": "Tailscale Sync",
"enabled": 1,
"schedule": 15,
"config": { "api_key": "**REDACTED**", "tailnet": "example.com" },
"runs": [
{
"id": 42,
"started_at": "2024-03-10T14:22:00",
"ended_at": "2024-03-10T14:22:01",
"status": "success",
"result": "3 updated of 12"
}
]
}
PUT /api/jobs/:id
Updates a job's enabled state, schedule interval, or configuration. All fields are optional — omitted fields are left unchanged. Config is merged with the existing config (partial updates are safe).
| Status | Condition |
|---|---|
200 |
Updated successfully — returns the updated job |
400 |
ID is not a valid integer |
404 |
No job with that ID |
Request body:
| Field | Type | Notes |
|---|---|---|
enabled |
boolean | true to enable, false to disable |
schedule |
integer | Interval in minutes (default: 15) |
config |
object | Merged with existing config — see per-job fields below |
Config fields by job key:
| Key | Field | Notes |
|---|---|---|
tailscale_sync |
api_key |
Tailscale API key |
tailscale_sync |
tailnet |
Tailnet name (e.g. example.com) |
tailscale_sync |
run_on_create |
true to also run when a new instance is created |
patchmon_sync |
api_url |
Full URL to the Patchmon hosts endpoint |
patchmon_sync |
api_token |
key:secret credential or pre-encoded base64 |
semaphore_sync |
api_url |
Full URL to the Semaphore inventory endpoint |
semaphore_sync |
api_token |
Semaphore API token |
POST /api/jobs/:id/run
Triggers a job to run immediately. Blocks until the job completes and returns the result. The run is recorded in the job's history regardless of outcome.
| Status | Condition |
|---|---|
200 |
Job completed successfully |
400 |
ID is not a valid integer |
404 |
No job with that ID |
500 |
Job handler threw an error (run is logged with status: "error") |
{ "summary": "3 updated of 12" }
Backup
GET /api/export
Downloads a JSON backup of all instances as a file attachment.
{
"version": 1,
"exported_at": "2024-03-10T14:22:00.000Z",
"instances": [ ... ]
}
POST /api/import
Replaces all instances from a JSON backup. Validates every row before committing — if any row is invalid the entire import is rejected.
| Status | Condition |
|---|---|
200 |
Import successful — returns { "imported": N } |
400 |
Body missing instances array, or validation errors |