Docs REST API Reference

REST API

The Gretl daemon exposes a JSON HTTP API on http://127.0.0.1:11611. Every CLI and SDK command goes through it. Bind address and port are configurable in ~/.gr/config.toml; loopback only by default.

Conventions

Authentication

By default the API is unauthenticated — it only listens on loopback. If you set auth.token in ~/.gr/config.toml, every request must carry it as Authorization: Bearer <token>.


Services

GET/v1/servicesList all services

Returns every registered service, regardless of state.

Response 200

{
  "services": [
    {
      "name":    "jobs-server",
      "port":    7401,
      "group":   "@jobs",
      "status":  "running",
      "pid":     38219,
      "url":     "http://localhost:7401",
      "startedAt": "2026-04-30T09:42:11+02:00",
      "cpu":     0.051,
      "rssMB":   142
    }
  ]
}
GET/v1/services/{name}Get one service

Response 200

{
  "name":   "jobs-server",
  "port":   7401,
  "status": "running",
  ...
}

Errors

404 unknown_service — no service registered under that name.

POST/v1/servicesRegister a service

Creates or updates a service. Idempotent on name.

Request body

{
  "name":    "jobs-server",
  "port":    7401,
  "cmd":     "npm run dev:server",
  "cwd":     "/Users/d/dev/jobs",
  "group":   "@jobs",
  "ready":   "http://localhost:7401/health",
  "env":     { "NODE_ENV": "development" }
}

Response 201

{
  "name":   "jobs-server",
  "status": "stopped",
  ...
}
POST/v1/services/{name}/startStart a service

Returns once the ready-check passes (or 502 if it doesn't within ?timeout=ms, default 30000).

Response 200

{ "name": "jobs-server", "status": "running", "pid": 38219 }
POST/v1/services/{name}/stopStop a service

Sends SIGTERM, then SIGKILL after ?grace=ms (default 5000).

DELETE/v1/services/{name}Unregister a service

Stops the service if running, then forgets about it.

Resolution

GET/v1/resolve/{name}Name → URL

The hot path used by SDKs. Returns the URL even if the service is stopped (so you can decide what to do).

Response 200

{
  "name":    "jobs-server",
  "url":     "http://localhost:7401",
  "port":    7401,
  "status":  "running"
}

Groups

GET/v1/groupsList groups
{
  "groups": [
    { "name": "@jobs", "services": 3, "running": 3 },
    { "name": "@data", "services": 2, "running": 2 }
  ]
}
POST/v1/groups/{name}/startStart a group

Brings up every service in the group in dependsOn order. Returns once all ready-checks pass.

POST/v1/groups/{name}/stopStop a group

Reverse-order stop.

Events (SSE)

GET/v1/eventsServer-Sent Events stream

Long-lived stream of lifecycle events. The desktop app, Chrome extension, and SDKs all consume this.

Event types

type Fields When
service.registered name, port, group POST /v1/services
service.started name, pid, url Process is up & ready
service.stopped name, exitCode Clean exit or SIGTERM
service.crashed name, exitCode, signal Non-zero exit, no stop request
service.stats name, cpu, rssMB Every 2s for running services
port.conflict port, claimedBy, holder Two services want the same port

Example frame

event: service.crashed
id: 4429
data: { "name": "jobs-worker", "exitCode": 137, "signal": "SIGKILL" }

Replay

Pass Last-Event-ID to replay events you missed during a reconnect; the daemon retains the last 1024.

Kubernetes

All K8s endpoints are on the cloud API at https://api.gretl.dev/k8s. Authenticate with Authorization: Bearer <GR_TOKEN>.

GET/k8s/clustersList clusters

Response 200

[{
  "id":         "abc123...",
  "name":       "prod-eks",
  "provider":   "eks",
  "in_cluster": false,
  "created_at": "2026-05-01T10:00:00Z"
}]
POST/k8s/clustersRegister a cluster

Request body

{
  "name":       "prod-eks",
  "provider":   "eks",
  "kubeconfig": "<base64-or-raw-kubeconfig>",
  "in_cluster": false
}

Response 201

{
  "id":   "abc123...",
  "name": "prod-eks"
}

Pass in_cluster: true (no kubeconfig needed) when running the Gretl agent inside the same cluster as a pod.

POST/k8s/clusters/{id}/syncSync workloads

Discovers Deployments and StatefulSets in all namespaces and upserts them into Gretl. Returns the count synced.

Response 200

{ "synced": 14 }
GET/k8s/workloadsList all workloads

Returns all workloads across every registered cluster. Filter by cluster with ?cluster_id=.

Response 200

[{
  "id":                    "def456...",
  "name":                  "langgraph-agent",
  "namespace":             "agents",
  "kind":                  "Deployment",
  "replicas":              0,
  "desired_replicas":      2,
  "auto_sleep_enabled":    true,
  "idle_timeout_minutes":  30,
  "last_activity_at":      "2026-05-17T23:11:00Z"
}]
POST/k8s/workloads/{id}/sleepScale to 0

Saves desired_replicas then patches the Deployment/StatefulSet to 0. Idempotent.

Response 200

{ "replicas": 0 }
POST/k8s/workloads/{id}/wakeRestore replicas

Scales back to desired_replicas (or 1 if never set).

Response 200

{ "replicas": 2 }
POST/k8s/workloads/{id}/tetherCreate a port-forward tether

Creates a tether record and returns the tether ID. Call /activate to open the TCP proxy.

Request body

{
  "container_port": 8000,
  "label": "HTTP API"
}

Response 201

{
  "tether_id":  "ghi789...",
  "local_port": 21042
}
POST/k8s/tethers/{id}/deactivateClose a port-forward tether

Destroys open sockets, closes the TCP listener, and releases the local port back to the pool.

Response 200

{ "ok": true }
GET/k8s/workloads/{id}/auto-sleepGet auto-sleep settings
PATCH/k8s/workloads/{id}/auto-sleepUpdate auto-sleep settings

PATCH body

{
  "auto_sleep_enabled":   true,
  "idle_timeout_minutes": 30
}

Response 200

{
  "auto_sleep_enabled":   true,
  "idle_timeout_minutes": 30
}

The auto-sleep sweeper runs every 10 minutes. Activity is tracked via tether connections — each new TCP connection to the port-forward updates last_activity_at.

Errors

Errors are JSON with a stable code and human-readable message:

{
  "code":    "port_in_use",
  "message": "port 7401 is held by pid 12044 (chrome)",
  "hint":    "run 'gr adopt 7401 --as <name>' or 'gr kill :7401'"
}
HTTP code Meaning
400 invalid_spec Missing required field, or invalid shape.
401 unauthorized Auth token missing or wrong.
404 unknown_service No service registered with that name.
409 port_in_use The port is held by another process.
409 already_running Service is already in running state.
502 ready_timeout Process started but ready-check didn't pass.
503 daemon_busy Daemon is doing a long-running op; retry shortly.