Databases API
Provision PostgreSQL and MySQL, fetch connection strings, manage backups, replicas, forks and IP allowlists.
SMLL runs PostgreSQL (CloudNativePG) and MySQL (Percona XtraDB Cluster). The API covers provisioning, credentials, backups, restores, replicas and forks.
All paths below are relative to:
https://api.smll.io/api/v1/workspaces/{workspaceID}/vpcs/{vpcID}/databasesEndpoints
| Method | Path | Role | Purpose |
|---|---|---|---|
GET | / | Member | List databases in the VPC |
POST | / | Admin | Provision a database |
GET | /{databaseID} | Member | Database detail, with live status |
PATCH | /{databaseID} | Admin | Rename, resize, toggle external access |
DELETE | /{databaseID} | Admin | Delete the database |
GET | /{databaseID}/connection | Member | Connection details and credentials |
GET | /{databaseID}/backups | Member | List backups |
POST | /{databaseID}/backups | Admin | Take a backup now |
POST | /{databaseID}/backups/restore | Admin | Restore into a new database |
PATCH | /{databaseID}/backups/schedule | Admin | Configure the backup schedule |
DELETE | /{databaseID}/backups/{backupName} | Admin | Delete a backup |
POST | /{databaseID}/fork | Admin | Fork to a new database |
PATCH | /{databaseID}/replicas | Admin | Change the read replica count |
GET | /{databaseID}/schema | Admin | Inspect tables and columns |
POST | /{databaseID}/query | Admin | Run a SQL query |
GET | /{databaseID}/slow-queries | Member | Slow query log (accepts limit) |
POST | /{databaseID}/slow-queries/reset | Admin | Clear slow query statistics |
GET | /{databaseID}/ip-allowlist | Member | List allowlist rules |
POST | /{databaseID}/ip-allowlist | Admin | Add a rule |
DELETE | /{databaseID}/ip-allowlist/{ruleID} | Admin | Remove a rule |
Provision a database
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
-d '{
"name": "app-db",
"engine": "postgresql",
"instance_type": "smll.small",
"storage_gb": 10,
"postgres_version": "16",
"replicas": 1,
"external_access_enabled": true
}' \
"$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases"| Field | Type | Default | Notes |
|---|---|---|---|
name | string | required | Unique in the VPC, 19 characters or fewer |
engine | string | postgresql | postgresql or mysql |
instance_type | string | see notes | smll.nano through smll.xlarge. Defaults to smll.nano, or smll.small for MySQL |
storage_gb | integer | Disk size | |
postgres_version | string | PostgreSQL major version | |
mysql_version | string | MySQL version, when engine is mysql | |
replicas | integer | See the note below | |
external_access_enabled | boolean | Expose the database off-platform |
MySQL has two constraints PostgreSQL does not. It cannot run on smll.nano, because Galera needs more memory than that plan provides, and replicas must be an odd number (1, 3, 5) so the cluster can hold quorum. Even counts are rejected.
Provisioning is asynchronous. The response comes back with status: "creating"; poll the detail endpoint until it reads running.
Connection details
curl -s -H "$AUTH" \
"$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/connection"The response carries both routes to the database:
| Field | Meaning |
|---|---|
host, port, database, username, password | In-cluster connection parts |
database_url | Ready-made internal connection string |
pooler_host, pooler_host_ro | PgBouncer endpoints (PostgreSQL) |
read_only_host, read_only_url | Route reads to a replica |
external_host, external_host_ro | Reachable from outside the platform |
Use the internal host from services in the same VPC: it stays inside the network and skips the edge proxy. Use the external host from your laptop, CI, or anything off-platform.
External access has to be switched on (external_access_enabled), and is then reachable at a per-database subdomain:
postgresql://app:<password>@<shortID>.eu-central-1.database.smllapp.com:5432/<shortID>
mysql://app-<shortID>:<password>@<shortID>.eu-central-1.mysql.smllapp.com:3306/<shortID><shortID> is the first 8 characters of the database UUID.
The connection endpoint returns the live password in plain text, to any workspace member including Viewers. Treat a call to it as handing out full database access, and prefer wiring services up with a shared secret over passing credentials around.
Backups and restores
Take a backup on demand:
curl -s -X POST -H "$AUTH" \
"$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/backups"Restores do not overwrite the source. A restore provisions a new database from the backup, which is why the request takes a name:
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
-d '{"name":"app-db-restored","backup_name":"..."}' \
"$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/backups/restore"| Field | Required | Notes |
|---|---|---|
name | Yes | Name for the new database |
backup_name | For MySQL | Which backup to restore from |
target_time | No | Point in time to recover to, for PostgreSQL |
If the name is already taken in the VPC you get 409. If no completed backup exists yet, the platform triggers one and returns 409 telling you to retry shortly.
That shape is deliberate: recovery never destroys the thing you are recovering from. Point your application at the restored database once you have checked it.
Forks
A fork gives you a second database seeded from this one, for a staging environment or a migration rehearsal:
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
-d '{"name":"app-db-staging"}' \
"$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/fork"| Field | Required | Notes |
|---|---|---|
name | Yes | Name for the forked database |
target_vpc_id | No | Fork into a different VPC, defaults to this one |
A fork is a restore in friendlier clothes: it takes the latest backup, triggering a fresh one first if none exists, and restores it into a new cluster. So the copy reflects the last backup rather than the parent's state this instant, and a fork of a busy database can take a while. It is a fully independent database from the moment it exists, and writes to it never reach the parent.
Replicas
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
-d '{"replicas":2}' \
"$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/replicas"Replicas serve reads through read_only_host. Each one is a running instance of the same plan and is metered as such, so check the cost estimator before scaling up.
Queries and schema
GET /schema returns tables and columns. POST /query runs SQL and returns rows. Both require Owner or Admin, because they can read and modify any data in the database.
These back the dashboard's SQL console. For application traffic, connect directly with a normal driver rather than proxying queries through the API.
IP allowlists
With external access on, the database is reachable from the internet and is protected by its password. An allowlist narrows that to known addresses:
curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
-d '{"cidr":"203.0.113.4/32","description":"office"}' \
"$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/ip-allowlist"An empty allowlist means no IP restriction, not a closed door. Adding the first rule is what starts the filtering, so add your own address before you add anything else, or you will lock yourself out.
Status values
| Status | Meaning |
|---|---|
creating | Being provisioned |
running | Accepting connections |
error | The cluster failed to reach a healthy state |
deleting | Teardown in progress |
deleted | Soft-deleted, excluded from lists |
Status is resynced against the live cluster whenever you list or read a database, so it reflects the cluster rather than the last thing the API was told.