Base URL: https://api.surfra.io. Every route takes Authorization: Bearer $SURFRA_API_KEY. Request and response bodies are JSON.
{app} is an app’s name or its sfa_ id, except on the monitoring routes, which take the id.
| Method and path |
What it does |
GET /v1/apps |
List apps: {"apps": [...]} |
GET /v1/apps/{app} |
Read one app |
PUT /v1/apps/{app} |
Update the app. A name that does not exist yet creates it |
POST /v1/apps |
Create an app from a body with a name |
POST /v1/apps/{app}/run |
Queue one request: {"input": {...}}. Returns the transaction |
App body:
"image": "ghcr.io/acme/model:1",
"runtime": {"engine": "python"},
"env": {"MODEL_ID": "Qwen/Qwen3-0.6B"},
"registry": {"docker_secret_id": "ghcr"},
"autoscale": {"max_replicas": 2, "providers": [{"provider": "salad", "gpu": "rtx3090", "priority": 1}]}
See Autoscale fields for autoscale.
| Method and path |
What it does |
POST /v1/transactions |
Queue one or more steps |
GET /v1/transactions/{id} |
Status and each step’s output |
{"name": "transcribe", "app_name": "transcribe", "depends_on": [], "input": {"url": "..."}},
{"name": "summarize", "app_name": "summarize", "depends_on": ["transcribe"]}
"metadata": {"source": "nightly"}
A transaction’s status is running, succeeded, or failed. Each step has status, progress, and output. A failed step’s output is {"error": "..."}.
| Path |
What it does |
/openai/{app}/{path} |
Queue a request to an HTTP app’s OpenAI-compatible server, wait, and return its response |
/http/{app}/{path} |
The same for any path on an HTTP app’s server |
/v1/apps/{app}/http/{path} |
Forward straight to a running replica. Needs http_ports |
Gateways accept GET, POST, PUT, PATCH, and DELETE. Queued gateways take JSON bodies up to 8 MB and wait up to execution_timeout_sec.
| Method and path |
What it does |
GET /v1/workflows |
List workflows |
GET /v1/workflows/{name} |
Read one |
PUT /v1/workflows/{name} |
Save {"stages": [{"name", "app", "depends"}]} |
DELETE /v1/workflows/{name} |
Delete |
GET /v1/workflows/{name}/estimate |
Hourly rate of the apps in the workflow |
| Request |
What it does |
POST /v1/jobs |
Submit a job. Body: image, entrypoint, args, providers, env, secret, docker_secret, deadline_sec, liveness_sec, grace_sec, call_apps, deny_apps, spec, name, submission_id |
GET /v1/jobs?limit=50&offset=0&status= |
Recent jobs: {"total", "jobs": [...]} |
GET /v1/jobs/{id} |
One job. env_keys maps each variable name to spec or submit, without values |
POST /v1/jobs/{id}/stop |
Stop it: SIGTERM, then SIGKILL after grace_sec |
GET /v1/jobs/{id}/logs?after=&before=&q=&stream=&limit=200&order=desc |
Log lines: {"lines": [{"t", "s", "m"}]}. order=asc reads oldest first |
GET /v1/job-specs |
Saved job specs: {"specs": [...]} |
GET /v1/job-specs/{name} |
One spec |
PUT /v1/job-specs/{name} |
Save a spec with the same fields as a submit |
DELETE /v1/job-specs/{name} |
Delete a spec |
| Method and path |
What it does |
GET /v1/apps/{id}/observe |
Workers, replicas, and logs |
GET /v1/apps/{id}/metrics?window=24h&bucket=900 |
Chart data. bucket is seconds |
GET /v1/apps/{id}/requests?limit=20&offset=0 |
Recent requests |
GET /v1/apps/{id}/requests/{request_id} |
One request with input and output |
GET /v1/apps/{id}/logs |
Log lines, newest first: {"lines": [{"t", "s", "m", "r", "replica"}], "more"} |
/logs takes these query parameters:
| Parameter |
Meaning |
after, before |
ISO 8601 times. Default: the last 24 hours, which is everything kept |
q |
Words that every line must contain |
request |
A request id |
replica |
A replica id |
stream |
out, err, or sys |
limit |
Lines to return, up to 1000. Default 200 |
| Method and path |
What it does |
GET /v1/org |
The organization, its members, invites, and active invite links |
POST /v1/org/invites |
Invite an email address |
POST /v1/org/links |
Create an invite link: {"role": "member", "days": 7, "max_uses": null}. Returns the token once. Owners and admins |
DELETE /v1/org/links/{id} |
Revoke an invite link |
GET /v1/join/{token} |
The organization and role a link joins. No API key needed |
POST /v1/join/{token} |
Join the organization as the signed-in user |
| Method and path |
What it does |
GET /v1/secrets |
List stored credentials, without their values |
POST /v1/secrets |
Store a credential |
DELETE /v1/secrets/{id} |
Delete one |
GET /v1/clouds |
List connected cloud accounts, without their keys |
PUT /v1/clouds/{provider} |
Connect or replace a cloud account. Body: {"values": {...}}. Owners and admins |
DELETE /v1/clouds/{provider} |
Remove a cloud account. 409 while replicas run on it |
GET /v1/keys |
List API keys by prefix |
POST /v1/keys |
Create an API key. The token is shown once |
Errors return {"detail": "..."}.
| Status |
Meaning |
400 |
The body is invalid. detail names the field |
401 |
Missing or invalid API key |
404 |
Unknown app, workflow, or transaction |
409 |
The name belongs to another resource, the app does not expose HTTP, or a cloud still has running replicas |
413 |
Gateway body larger than 8 MB |
504 |
Gateway call waited longer than execution_timeout_sec |