API Overview
The SMLL REST API - base URL, authentication, resource hierarchy, errors and rate limits.
Everything the dashboard and the CLI do, they do by calling the SMLL REST API. The same API is available to you, so anything you can click you can also script.
The base URL is:
https://api.smll.ioAll authenticated endpoints live under /api/v1.
Quick start
Create an API key from Settings > API Keys (or smll api-keys create), then:
export SMLL_API_KEY=sk-...
AUTH="Authorization: Bearer $SMLL_API_KEY"
# Who am I?
curl -s -H "$AUTH" https://api.smll.io/api/v1/me
# What can I reach?
curl -s -H "$AUTH" https://api.smll.io/api/v1/workspacesResource hierarchy
Almost every endpoint is addressed by where the resource lives, not by ID alone. Resources nest three levels deep:
workspace billing, quotas, members, API keys
└── VPC an isolated network + namespace
├── services containers you deploy
├── databases PostgreSQL and MySQL
├── caches Redis-compatible
├── queues NATS
├── buckets S3-compatible object storage
├── secrets shared config
└── registry private container imagesThat shape shows up directly in the paths:
/api/v1/workspaces/{workspaceID}/vpcs/{vpcID}/services/{serviceID}So most calls need a workspace ID and a VPC ID before the resource ID. You can list your way down from /api/v1/workspaces, or read the IDs out of the dashboard URL. The CLI stores them for you after smll context set.
A VPC is scoped to one region. There is no cross-region endpoint: to work in two regions, create a VPC in each.
Authentication
Every request under /api/v1 needs a bearer token:
Authorization: Bearer <token>Three kinds of token are accepted, and they are interchangeable at the endpoint level:
| Token | Looks like | Used by |
|---|---|---|
| API key | sk- + 64 hex chars | Scripts, CI, integrations |
| CLI session token | hex string | smll login |
| Supabase JWT | three dot-separated parts | The dashboard |
For anything automated, use an API key. See Authentication for creating, scoping, and revoking them.
Permissions
Requests are authorised against your role in the workspace that owns the resource:
| Role | Read | Create, update, delete |
|---|---|---|
| Owner | Yes | Yes |
| Admin | Yes | Yes |
| Developer | Yes | No |
| Viewer | Yes | No |
Read endpoints accept any member of the workspace. Most write endpoints require Owner or Admin, and return 403 insufficient workspace permissions otherwise. The per-endpoint tables in this section mark the ones that need it.
An API key carries the role of the user who created it, so a key made by a Developer cannot deploy.
Requesting a VPC that belongs to a different workspace returns 403, even if you are a member of both. The VPC ID and the workspace ID in the path have to agree.
Requests
Send JSON, and say so:
Content-Type: application/jsonRequest bodies are capped at 1 MB. Uploads that exceed that (source archives, objects) go through their own endpoints, which hand back a presigned URL you upload to directly.
Unknown fields in a body are ignored rather than rejected.
Responses
Successful responses are JSON. List endpoints return a bare array:
[
{ "id": "3f9c...", "name": "api", "status": "running" },
{ "id": "7a21...", "name": "worker", "status": "running" }
]Single resources return an object. Deletes usually return {"status":"deleted"} or 204.
Most list endpoints return the whole collection, because these collections are naturally small (the resources in one VPC). The few that can grow without bound - logs, builds, deployments, transactions, slow queries - accept a limit query parameter.
Timestamps are RFC 3339 in UTC (2026-08-20T18:57:21Z). IDs are UUIDs. Money is GBP, as a decimal number.
Errors
Errors carry a non-2xx status and a JSON body with one key:
{ "error": "insufficient workspace permissions" }| Status | Meaning |
|---|---|
400 | Malformed body, or a missing or invalid field |
401 | Missing, invalid, expired or revoked token |
403 | Authenticated, but not allowed - wrong role, or resource in another workspace |
404 | No such resource, or it is soft-deleted |
409 | Name already in use in this VPC |
429 | Rate limited |
503 | The underlying infrastructure is unavailable |
503 infrastructure is unavailable means the platform could not reach Kubernetes. Creates fail closed on purpose: no record is written for a resource that could not be built, so a retry is safe and will not leave a duplicate behind.
Rate limits
The general limit on /api/v1 is 100 requests per minute, applied per client IP, with a burst of 100. Some endpoints are tighter: the public blog API allows 30 per minute, and webhooks 30 per minute.
Exceeding it returns 429 with a Retry-After header giving the seconds to wait.
There is no rate limit header on successful responses, so treat 429 plus Retry-After as the signal and back off from there.
Versioning
The version lives in the path (/api/v1). Fields are added to responses over time, so parse leniently and ignore what you do not recognise. Removals and behaviour changes get a new version.