Files
Catalyst/docs/api.md
T
joshandClaude Sonnet 4.6 7f6ce42d9a docs: move API reference to docs/api.md and add Jobs endpoints
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>
2026-07-04 20:15:07 -04:00

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