Skip to main content

Management API

REST

REST API for deploying apps, provisioning databases, and managing infrastructure on the Sylphx Platform.

Base URL: https://sylphx.com/api/v1

11 Resource Groups

Full platform coverage

50+ Endpoints

Everything you need

OpenAI-Compatible

Drop-in AI proxy

Bearer Auth

slx_cli_* tokens

Authentication

All endpoints require a Bearer token. Generate tokens at Dashboard → Settings → API Keys. Tokens are scoped to an organisation.

Authorization: Bearer slx_cli_xxxxxxxxxxxxxxxx

For the AI endpoints, set this token as your api_key and base_url = https://sylphx.com/api/v1 in any OpenAI client library.

Method colours:GETPOSTDELETEPUTPATCH uses same colour as PUT

Projects

Deploy and manage applications. Each project maps to a K8s Deployment.

GET/api/v1/projects

List all projects in the organisation.

Response

[
  {
    "id": "uuid",
    "slug": "my-app",
    "name": "My App",
    "description": "...",
    "isActive": true,
    "environments": [...]
  }
]
POST/api/v1/projects

Create a new project. Use Option A (Git-based) or Option B (pre-built Docker image).

Request Body

FieldTypeDescription
name*stringDisplay name
slug*stringURL-safe identifier
gitRepositorystringGitHub repo (owner/repo). Option A.
gitBranchstringBranch to build (e.g. main). Option A.
buildPack"dockerfile" | "nixpacks"Build strategy. Option A.
dockerImagestringPre-built image reference. Option B.
portnumberContainer port to expose.
domainstringHostname (e.g. myapp.sylphx.app).

* required

Response

// 201 Created
{
  "id": "uuid",
  "slug": "my-app",
  "name": "My App",
  "isActive": true
}
GET/api/v1/projects/:id

Get a project by ID.

PATCH/api/v1/projects/:id

Update project settings (name, description, domain, port, etc.).

Request Body

FieldTypeDescription
namestringNew display name.
descriptionstringProject description.
domainstringCustom hostname.
portnumberContainer port.

* required

DELETE/api/v1/projects/:id

Delete a project and all its resources (services, env vars, storage bindings).

Response

// 200 OK
GET/api/v1/projects/:id/status

Get live Kubernetes status for a project.

Response

{
  "status": "running",
  "environment": "production",
  "replicas": { "ready": 2, "desired": 2 }
}
GET/api/v1/projects/:id/logs

Stream or fetch container logs. Query params: envType (default production), tail (default 200, max 1000), stream=true for SSE streaming, deploymentId to scope to a specific build.

Response

// Newline-delimited log lines (stream=false)
2026-03-09T11:00:00Z [web] Server listening on :3000
2026-03-09T11:00:01Z [web] GET /healthz 200

// Server-Sent Events (stream=true)
data: {"ts":"2026-03-09T11:00:00Z","level":"info","msg":"Server listening on :3000"}
POST/api/v1/projects/:id/deploy

Trigger a rolling deployment. Rebuilds the image from Git (if gitRepository is set) or re-deploys with updated env vars. Uses Kaniko in-cluster for Docker builds.

💡 Always call this after updating env vars to apply changes.

Request Body

FieldTypeDescription
envType"production" | "staging" | "development"Target environment. Default: production.

* required

Response

{
  "deploymentId": "uuid",
  "status": "triggered",
  "environment": "production"
}
POST/api/v1/projects/:id/rollback

Roll back to a specific previous deployment. Traffic switches atomically — zero downtime.

💡 Rolling back does not revert env var changes — update env vars separately if needed.

Request Body

FieldTypeDescription
deploymentId*stringID of the deployment to restore. Get IDs from GET /deployments.

* required

Response

{
  "deploymentId": "uuid",
  "status": "rolling-back",
  "targetDeploymentId": "uuid"
}
GET/api/v1/projects/:id/deployments

List deployment history. Query params: envType, limit (default 20, max 100).

Response

[
  {
    "id": "uuid",
    "status": "success",
    "gitCommit": "abc1234",
    "gitBranch": "main",
    "triggeredBy": "push",
    "startedAt": "2026-03-09T11:00:00Z",
    "finishedAt": "2026-03-09T11:02:30Z"
  }
]

Environment Variables

Secrets stored encrypted in the Platform database. Injected into the container as env vars on every deploy. Values are never exposed in plaintext in list responses.

GET/api/v1/projects/:id/env-vars

List env vars for a project. Query params: envType (default production), envId. Secret values are masked as "***".

Response

[
  { "key": "DATABASE_URL", "value": "***", "secret": true },
  { "key": "PORT", "value": "3000", "secret": false }
]
POST/api/v1/projects/:id/env-vars

Set (upsert) one or more env vars. This does NOT restart the container — call POST /deploy after to apply.

💡 After setting vars, call POST /projects/:id/deploy to apply them to the running container.

Request Body

FieldTypeDescription
envType*"production" | "staging" | "development"Target environment.
vars*Array<{ key: string; value: string; secret?: boolean }>Variables to set. secret=true masks values in the dashboard and logs.

* required

Environments

Manage deployment environments per project (production, staging, preview). Each environment has its own env vars, replicas, and domain.

GET/api/v1/projects/:id/environments

List all environments for a project.

Response

[
  { "id": "uuid", "name": "production", "domain": "myapp.sylphx.app", "replicas": 2 },
  { "id": "uuid", "name": "staging", "domain": "staging-myapp.sylphx.app", "replicas": 1 }
]
POST/api/v1/projects/:id/environments

Create a new environment for a project.

Request Body

FieldTypeDescription
name*stringEnvironment name (e.g. "staging").
domainstringCustom hostname for this environment.
replicasnumberNumber of replicas. Default: 1.

* required

GET/api/v1/projects/:id/environments/:envId

Get a specific environment by ID.

PATCH/api/v1/projects/:id/environments/:envId

Update environment settings (domain, replicas, etc.).

Request Body

FieldTypeDescription
domainstringNew hostname.
replicasnumberNew replica count.

* required

POST/api/v1/projects/:id/environments/:envId/provision

Provision infra for a new environment (creates K8s namespace, secrets, HTTPRoute).

Services

A project can have multiple named services (e.g. web, worker, scheduler). Each service is an independent K8s Deployment with its own image, resources, and env.

GET/api/v1/projects/:id/services

List all services for a project.

Response

[
  { "id": "uuid", "name": "web",    "port": 3000, "replicas": 2, "status": "running" },
  { "id": "uuid", "name": "worker", "port": null,  "replicas": 1, "status": "running" }
]
POST/api/v1/projects/:id/services

Add a new named service to a project.

Request Body

FieldTypeDescription
name*stringService name (e.g. "worker").
portnumberHTTP port to expose. Omit for background workers.
dockerfilePathstringPath to Dockerfile. Default: Dockerfile.
replicasnumberReplica count. Default: 1.

* required

GET/api/v1/projects/:id/services/:name

Get a specific service by name.

PATCH/api/v1/projects/:id/services/:name

Update service configuration (replicas, port, resources).

PATCH/api/v1/projects/:id/services/:name/environments/:envId

Override service settings for a specific environment.

GET/api/v1/services/:serviceId/latest-image

Get the latest built image tag for a service (SHA-tagged from Harbor).

Response

{ "image": "registry.sylphx.com/sylphx/my-app:abc123def456" }
GET/api/v1/services/:serviceId/tcp-proxy

Get the TCP proxy config for a service (used to expose non-HTTP services).

Response

{
  "enabled": true,
  "externalPort": 5432,
  "targetPort": 5432,
  "allowlist": ["203.0.113.0/24"]
}
POST/api/v1/services/:serviceId/tcp-proxy

Enable TCP proxy for a service (e.g. expose a database port externally).

Request Body

FieldTypeDescription
externalPort*numberNodePort to expose externally.
targetPort*numberContainer port to forward to.

* required

DELETE/api/v1/services/:serviceId/tcp-proxy

Disable the TCP proxy for a service. Add ?permanent=true to remove the NodePort allocation.

GET/api/v1/services/:serviceId/tcp-proxy/allowlist

List IP allowlist rules for the TCP proxy.

POST/api/v1/services/:serviceId/tcp-proxy/allowlist

Add an IP CIDR to the allowlist.

Request Body

FieldTypeDescription
cidr*stringCIDR block (e.g. "203.0.113.0/24").
descriptionstringHuman-readable label.

* required

DELETE/api/v1/services/:serviceId/tcp-proxy/allowlist/:ruleId

Remove an allowlist rule.

Resources (Unified API)

Unified Management API for ALL resource kinds: databases (PostgreSQL/MySQL), KV stores (Redis), blob storage, search (Typesense), and volumes. One API, one auth model — works with both slx_cli_* tokens and browser cookies.

GET/api/v1/resources

List all resources. Filter by kind using ?kind=database|kv|blob|search|volume.

Response

{
  "object": "list",
  "data": [
    { "id": "uuid", "kind": "database", "name": "Production DB", "reconcileStatus": "synced", ... },
    { "id": "uuid", "kind": "kv",       "name": "My Redis",      "reconcileStatus": "synced", ... }
  ]
}
POST/api/v1/resources

Provision any resource kind. Returns 202 immediately — provisioning is async. Poll GET /resources/:id for status.

💡 Poll GET /resources/:id until reconcileStatus === "synced" (~60–90s). connectionString/password is only returned by the single-resource endpoint.

Request Body

FieldTypeDescription
name*stringDisplay name.
kind*"database" | "kv" | "blob" | "search" | "volume"Resource capability category.
providerstringUnderlying tech: database→postgresql|mysql, kv→redis|valkey, blob→b2|s3|r2, search→typesense, volume→ceph-rbd|ceph-fs. Platform picks a sensible default.
configobjectProvider-specific config (storageGb, maxMemoryMb, accessMode, etc.).
environmentIdstring (uuid)Optional: auto-bind to this environment after provisioning.

* required

Response

// 202 Accepted — provisioning started
{
  "id": "uuid",
  "kind": "database",
  "name": "Production DB",
  "provider": "postgresql",
  "reconcileStatus": "pending",
  "status": "pending",
  "message": "Database provisioning started. Poll GET /resources/:id for status.",
  "createdAt": "2026-03-09T11:00:00Z"
}
GET/api/v1/resources/:id

Get a resource including live status, connection string (database/kv), and provider-specific config.

Response

// Database example
{
  "id": "uuid",
  "kind": "database",
  "provider": "postgresql",
  "name": "Production DB",
  "status": "ready",
  "tier": "standard",
  "connectionString": "postgresql://app:secret@…:5432/app",
  "maskedConnectionString": "postgresql://app:***@…:5432/app",
  "publicConnectionString": "postgresql://app:[email protected]:5432/app",
  "publicHost": "abc123.sylphx.net",
  "backupRetentionDays": 14,
  "createdAt": "2026-03-09T11:00:00Z"
}
PATCH/api/v1/resources/:id

Update resource name or config fields.

DELETE/api/v1/resources/:id

⚠️ Deprovision and permanently delete the resource. Irreversible. Returns 204 No Content.

GET/api/v1/resources/:id/metrics

Get Prometheus metrics for a resource. Adapts by kind (CNPG metrics for databases, Redis metrics for kv).

Response

// Database (CNPG)
{
  "available": true,
  "kind": "database",
  "databaseSize": 1234567,
  "cacheHitRatio": 0.97,
  "activeConnections": 12,
  "queryThroughput": [{ "t": 1709985600, "v": 42.3 }]
}

// KV (Redis)
{
  "available": true,
  "kind": "kv",
  "memoryUsedBytes": 12345678,
  "memoryUsedPercent": 48.2,
  "hitRatio": 0.94,
  "connectedClients": 5,
  "commandsPerSec": [{ "t": 1709985600, "v": 120 }]
}
GET/api/v1/resources/:id/backups

List CNPG backup snapshots. kind=database only (CNPG clusters). Returns 400 for other kinds.

Response

{
  "available": true,
  "backups": [
    { "name": "pg-abc123-backup-20260309", "status": "completed", "completedAt": "2026-03-09T02:00:00Z", "method": "plugin" }
  ]
}
POST/api/v1/resources/:id/restore

PITR restore to a point in time. Creates a NEW resource — does not overwrite the source.

💡 ⚠️ Restore creates a NEW database resource. Poll GET /resources/:restoreId until reconcileStatus === "synced".

Request Body

FieldTypeDescription
targetTime*string (ISO 8601)Point-in-time to restore to (max 7 days ago).

* required

Response

// 202 Accepted
{
  "restoreId": "uuid",
  "restoredClusterName": "pg-abc123-r-1abc2d",
  "targetTime": "2026-03-09T10:00:00Z",
  "message": "Restore in progress, ready in ~5 min. Poll GET /resources/:restoreId for status."
}
POST/api/v1/resources/:id/branch

Clone a database for staging/preview. fast=true uses a Ceph VolumeSnapshot (copy-on-write, ~60s). fast=false = fresh empty cluster.

Request Body

FieldTypeDescription
namestringBranch resource name.
envstringTarget environment. Default: staging.
fastbooleantrue = snapshot clone (data copy), false = empty cluster.
storageGbnumberBranch storage size. Default: 10.

* required

Response

// 201 Created — new database resource
GET/api/v1/resources/:id/bindings

List all project environment bindings for this resource.

Response

{
  "bindings": [
    {
      "id": "uuid",
      "projectEnvironmentId": "uuid",
      "resourceType": "database",
      "role": "primary",
      "envName": "production",
      "projectName": "My App",
      "projectSlug": "my-app",
      "createdAt": "2026-03-09T11:00:00Z"
    }
  ]
}
POST/api/v1/resources/:id/bindings

Bind a resource to a project environment. Automatically syncs connection env vars (DATABASE_URL, REDIS_URL, etc.) to the deployment.

Request Body

FieldTypeDescription
projectEnvironmentId*string (uuid)Target project environment ID.
role"primary" | "replica" | "analytics" | "backup"Binding role. Default: primary.

* required

DELETE/api/v1/resources/:id/bindings/:bindingId

Remove a resource binding. Cleans up injected env vars from the deployment.

Storage (Blob)

S3-compatible blob storage backed by Backblaze B2, served via CDN at storage.sylphx.com.

GET/api/v1/projects/:id/storage

List storage resources attached to a project.

POST/api/v1/projects/:id/storage

Provision blob storage for a project and get S3-compatible credentials.

💡 The returned envVars should be added to your project env vars, then deploy.

Request Body

FieldTypeDescription
envType*"production" | "staging" | "preview"Environment.
namestringFriendly name. Default: "primary".

* required

Response

// 201 Created
{
  "id": "uuid",
  "publicUrl": "https://storage.sylphx.com/org/my-app/",
  "envVars": {
    "BLOB_ENDPOINT":        "https://s3.eu-central-003.backblazeb2.com",
    "BLOB_BUCKET":          "sylphx-storage-prod",
    "BLOB_PREFIX":          "org/my-app/",
    "BLOB_KEY_ID":          "...",
    "BLOB_APPLICATION_KEY": "...",
    "BLOB_PUBLIC_URL":      "https://storage.sylphx.com/org/my-app/"
  }
}
DELETE/api/v1/projects/:id/storage/:storageId

⚠️ Delete a storage resource and revoke credentials. Does not delete existing blobs.

Volumes (Persistent Storage)

Persistent Ceph RBD volumes (rook-ceph-block, RWO) attached to a service for stateful data. Use this for uploaded files, caches, or any data that must survive pod restarts.

GET/api/v1/projects/:id/volumes

List persistent volumes attached to a project.

Response

[
  { "id": "uuid", "name": "uploads", "mountPath": "/app/uploads", "sizeGb": 20, "status": "bound" }
]
POST/api/v1/projects/:id/volumes

Create and attach a persistent volume.

Request Body

FieldTypeDescription
name*stringVolume name (e.g. "uploads").
mountPath*stringPath inside the container to mount the volume.
sizeGb*numberVolume size in GB.

* required

GET/api/v1/projects/:id/volumes/:volId

Get a specific volume.

DELETE/api/v1/projects/:id/volumes/:volId

⚠️ Delete a volume and its PVC. All data on the volume is permanently lost.

Config

Key-value config store scoped to a project. For non-secret runtime configuration (feature switches, thresholds). Not injected as env vars — read via SDK or API.

GET/api/v1/projects/:id/config

List all config keys for a project.

Response

[
  { "key": "FEATURE_X", "value": "true" },
  { "key": "MAX_RETRIES", "value": "3" }
]
POST/api/v1/projects/:id/config

Set (upsert) multiple config keys at once.

Request Body

FieldTypeDescription
entries*Array<{ key: string; value: string }>Config entries to set.

* required

GET/api/v1/projects/:id/config/:key

Get a specific config key.

Response

{ "key": "FEATURE_X", "value": "true" }
PUT/api/v1/projects/:id/config/:key

Set (upsert) a single config key.

Request Body

FieldTypeDescription
value*stringValue to store.

* required

DELETE/api/v1/projects/:id/config/:key

Delete a config key.

AI / LLM

OpenAI-compatible API that proxies to configured LLM providers. Use your Sylphx token as the api_key.

POST/api/v1/chat/completions

OpenAI-compatible chat completions. Set base_url = https://sylphx.com/api/v1 in any OpenAI SDK.

Response

{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "choices": [{ "message": { "role": "assistant", "content": "..." } }],
  "usage": { "prompt_tokens": 12, "completion_tokens": 34, "total_tokens": 46 }
}
POST/api/v1/embeddings

Generate text embeddings. OpenAI-compatible format.

GET/api/v1/models

List all available models (chat + embedding).

Response

{
  "object": "list",
  "data": [
    { "id": "gpt-4o", "object": "model" },
    { "id": "claude-3-5-sonnet", "object": "model" },
    { "id": "text-embedding-3-small", "object": "model" }
  ]
}

Health

Platform health and runner status. No auth required.

GET/api/v1/health/status

Overall platform health check.

Response

{
  "status": "healthy",
  "services": {
    "database": "ok",
    "kubernetes": "ok",
    "storage": "ok"
  },
  "timestamp": "2026-03-09T11:00:00Z"
}
GET/api/v1/health/runners

GitHub ARC runner pool status — idle, busy, and offline counts.

Response

{
  "runners": [
    { "name": "runner-abc", "status": "idle",  "labels": ["linux", "x64"] },
    { "name": "runner-def", "status": "busy",  "labels": ["linux", "x64"] }
  ],
  "summary": { "idle": 1, "busy": 1, "offline": 0 }
}

Error Responses

All errors return a JSON body with error and optional details fields.

{ "error": "Human-readable message", "details": "..." }
StatusMeaning
400Bad request / invalid body
401Missing or invalid Bearer token
403Token valid but no access to this resource
404Resource not found
409Conflict (e.g. slug already taken)
500Internal server error

Quick Reference (curl)

BASE="https://sylphx.com/api/v1"
TOKEN="slx_cli_YOUR_TOKEN_HERE"
AUTH="Authorization: Bearer $TOKEN"

# ── Projects ──────────────────────────────────────────────
curl -H "$AUTH" $BASE/projects                                # list
curl -H "$AUTH" $BASE/projects/{ID}                           # get
curl -H "$AUTH" $BASE/projects/{ID}/status                    # live status
curl -H "$AUTH" "$BASE/projects/{ID}/logs?tail=200"           # logs

# Deploy (rebuild + rollout)
curl -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"envType":"production"}' $BASE/projects/{ID}/deploy

# Rollback to a previous deployment
curl -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"deploymentId":"DEPLOY_ID"}' $BASE/projects/{ID}/rollback

# ── Environment Variables ─────────────────────────────────
curl -H "$AUTH" "$BASE/projects/{ID}/env-vars?envType=production"
curl -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"envType":"production","vars":[{"key":"MY_VAR","value":"hello","secret":true}]}' \
  $BASE/projects/{ID}/env-vars
# (then deploy to apply)

# ── Resources (Unified API) ───────────────────────────────
# Provision a PostgreSQL database
curl -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name":"My DB","kind":"database","provider":"postgresql","config":{"env":"production","storageGb":10}}' \
  $BASE/resources                                             # provision (202)
curl -H "$AUTH" $BASE/resources/{ID}                          # get + connection string
curl -H "$AUTH" $BASE/resources/{ID}/metrics                  # metrics
curl -H "$AUTH" $BASE/resources/{ID}/backups                  # list backups (database only)
curl -X DELETE -H "$AUTH" $BASE/resources/{ID}                # ⚠️ delete

# Provision a Redis KV store
curl -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name":"My Redis","kind":"kv","provider":"redis","config":{"storageGb":5}}' \
  $BASE/resources                                             # provision (202)
curl -H "$AUTH" $BASE/resources/{ID}                          # get + connection string

# Filter resources by kind
curl -H "$AUTH" "$BASE/resources?kind=database"               # list all databases
curl -H "$AUTH" "$BASE/resources?kind=kv"                     # list all KV stores

# ── Storage ───────────────────────────────────────────────
curl -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"envType":"production","name":"primary"}' \
  $BASE/projects/{ID}/storage                                 # provision
curl -H "$AUTH" $BASE/projects/{ID}/storage                   # list

# ── AI / LLM ──────────────────────────────────────────────
curl -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}' \
  $BASE/chat/completions

# ── Health ────────────────────────────────────────────────
curl $BASE/health/status