API Reference
The Koddrio Cloud REST API gives you programmatic access to organizations, sites, domains, backups, cron, and SSH keys. Everything available in the CLI is available here.
Introduction
The API is organized around REST. It accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes and verbs.
All endpoints live under the /1.0 prefix:
https://api.koddrio.com/1.0/
Trailing slashes are optional: both /sites/ and /sites work.
Authentication
Authenticate with a bearer token in the Authorization header. Tokens are scoped to a user and inherit access to every organization that user belongs to.
curl https://api.koddrio.com/1.0/users/me/ \
-H "Authorization: Bearer kdo_live_a4f8c2..."
The fastest way to get a token is the CLI: kodo auth login. You can also create one programmatically by exchanging your email and password at POST /auth/login/.
Requests without a valid token return 401 Unauthorized. Tokens that have expired return the same.
Errors
Errors use conventional HTTP status codes and a consistent JSON shape:
{
"error": {
"code": "resource_missing",
"message": "No such site"
}
}
See error codes for the full list.
List responses
Endpoints that return a collection wrap the result in a list envelope. Lists are not paginated yet: every entity the caller has access to is returned in data.
{
"object": "list",
"data": [ /* ... */ ]
}
Short IDs
Every resource has a short, prefixed identifier derived from its underlying UUID:
org_4kR9x2mNpQw— organizationsite_3xRm8KpL2vN— sitedom_7yQp4nMxR3v— domainbkp_9zKx5pNqL2v— backupjob_2xMp8rQnKvL— jobtok_5pNxQ8mRvK2— API token
Most endpoints that take a site ID also accept the site's primary domain. So both /sites/site_3xRm8KpL2vN/ and /sites/example.com/ work.
Async jobs
State-changing operations like creating a site, restoring a backup, or applying config changes are asynchronous. The endpoint returns immediately with a job object you can poll until it reaches a terminal state.
"job": {
"id": "job_2xMp8rQnKvL",
"object": "job",
"type": "site.create",
"status": "queued",
"created": 1712534400
}
Poll GET /jobs/<id>/ until status is one of finished, failed, stopped, or canceled.
POST/auth/login/
Exchange an email and password for an API token. Returns the token's secret only once — store it securely.
curl https://api.koddrio.com/1.0/auth/login/ \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"..."}'
{
"token": {
"id": "tok_5pNxQ8mRvK2",
"object": "token",
"secret": "kdo_live_a4f8c2...",
"label": "login",
"created": 1712534400,
"expires": null
},
"user": {
"id": "usr_8mKp3xR9vNq",
"object": "user",
"email": "[email protected]"
}
}
GET/users/me/
Returns the user that owns the bearer token used in the request.
curl https://api.koddrio.com/1.0/users/me/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
{
"id": "usr_8mKp3xR9vNq",
"object": "user",
"email": "[email protected]",
"created": 1709856000
}
GET/tokens/
Lists every API token belonging to the current user. Token secrets are never returned after creation.
curl https://api.koddrio.com/1.0/tokens/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
POST/tokens/
Creates a new API token. Pass an optional label for organization, and expires as a unix timestamp for time-bound tokens.
curl https://api.koddrio.com/1.0/tokens/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{"label":"ci-deploy","expires":1735689600}'
DELETE/tokens/<id>/
Revokes a token. The token used to make the request cannot delete itself.
curl -X DELETE https://api.koddrio.com/1.0/tokens/tok_5pNxQ8mRvK2/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
GET/orgs/
Lists every organization the current user is a member of.
curl https://api.koddrio.com/1.0/orgs/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
{
"object": "list",
"data": [
{
"id": "org_4kR9x2mNpQw",
"object": "org",
"name": "Acme Widgets",
"created": 1709856000
}
]
}
POST/orgs/
Creates an organization. The current user is added as the owner.
curl https://api.koddrio.com/1.0/orgs/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{"name":"Acme Widgets"}'
GET/orgs/<id>/
Returns a single organization. The caller must be a member.
curl https://api.koddrio.com/1.0/orgs/org_4kR9x2mNpQw/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
GET/sites/
Lists every site the current user has access to. Pass ?org=org_... to scope the response to a single organization.
curl https://api.koddrio.com/1.0/sites/?org=org_4kR9x2mNpQw \
-H "Authorization: Bearer $KODDRIO_SECRET"
POST/sites/
Provisions a new WordPress site. Returns the created site, the generated admin credentials (only on creation), and a job you can poll for completion.
Required: org, region. Optional: config (any subset of config keys) and run (a list of WP-CLI commands to execute on first boot, used for blueprints).
curl https://api.koddrio.com/1.0/sites/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{
"org": "org_4kR9x2mNpQw",
"region": "eu-fra",
"config": {
"php.version": "8.4",
"php.workers": 4,
"php.memory": 256,
"cache.driver": "redis",
"cache.memory": 128
}
}'
{
"id": "site_3xRm8KpL2vN",
"object": "site",
"org": "org_4kR9x2mNpQw",
"status": "creating",
"site_url": "https://lazy-otter-042.koddrio.app",
"admin_user": "[email protected]",
"admin_password": "v8Lq...Xp2k",
"job": { "id": "job_2xMp8rQnKvL", "status": "queued" }
}
GET/sites/<id>/
Returns a single site. <id> can be the short ID or the site's primary domain.
curl https://api.koddrio.com/1.0/sites/example.com/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
PATCH/sites/<id>/
Updates one or more config keys. Pass null to reset a key to its default. Setting an environment.* key to null deletes the variable.
The response includes only the keys that actually changed, and a list of any background jobs queued to apply them. If nothing changed, changes is empty and no jobs run.
curl -X PATCH https://api.koddrio.com/1.0/sites/example.com/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{
"config": {
"php.workers": 8,
"php.memory": "512M",
"environment.WP_DEBUG": "true"
}
}'
{
"changes": {
"php.workers": 8,
"php.memory": 512,
"environment.WP_DEBUG": "true"
},
"jobs": [
{ "id": "job_2xMp8rQnKvL", "type": "site.update", "status": "queued" }
]
}
DELETE/sites/<id>/
Schedules a site for destruction. The site moves to deleting, then deleted once the job completes.
curl -X DELETE https://api.koddrio.com/1.0/sites/example.com/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
GET/domains/
Lists domains across every site the user has access to. Filter to a specific site with ?site=site_....
curl https://api.koddrio.com/1.0/domains/?site=site_3xRm8KpL2vN \
-H "Authorization: Bearer $KODDRIO_SECRET"
POST/domains/
Adds a domain to a site. Set primary to make it the canonical domain in WordPress.
curl https://api.koddrio.com/1.0/domains/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{
"site": "site_3xRm8KpL2vN",
"name": "example.com",
"primary": true
}'
PATCH/domains/<id>/
Promotes a secondary domain to primary. The previously-primary domain becomes secondary.
curl -X PATCH https://api.koddrio.com/1.0/domains/dom_7yQp4nMxR3v/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{"primary": true}'
DELETE/domains/<id>/
Removes a domain from a site. The site's internal *.koddrio.app domain cannot be removed.
curl -X DELETE https://api.koddrio.com/1.0/domains/dom_7yQp4nMxR3v/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
GET/backups/
Lists backups, optionally scoped to a single site with ?site=site_....
curl https://api.koddrio.com/1.0/backups/?site=example.com \
-H "Authorization: Bearer $KODDRIO_SECRET"
POST/backups/
Triggers an on-demand backup. Returns the new backup record and a job for the upload.
curl https://api.koddrio.com/1.0/backups/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{"site": "site_3xRm8KpL2vN"}'
GET/backups/<id>/
Returns a single backup including size and timestamps.
curl https://api.koddrio.com/1.0/backups/bkp_9zKx5pNqL2v/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
POST/backups/<id>/restore/
Restores a backup in place. The type field controls scope: full, files, or database.
curl https://api.koddrio.com/1.0/backups/bkp_9zKx5pNqL2v/restore/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{"type": "full"}'
POST/backups/<id>/export/
Generates a signed download URL for the backup archive. type may be full, files, or database. Returns a job whose result contains the download link.
curl https://api.koddrio.com/1.0/backups/bkp_9zKx5pNqL2v/export/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{"type": "database"}'
GET/cron/
Lists cron entries, optionally scoped to a site with ?site=site_.... Each entry has an ID, schedule, and command.
curl https://api.koddrio.com/1.0/cron/?site=example.com \
-H "Authorization: Bearer $KODDRIO_SECRET"
POST/cron/
Adds a cron entry. Schedules use standard 5-field crontab syntax. The command runs in the site's container as the site user.
curl https://api.koddrio.com/1.0/cron/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{
"site": "site_3xRm8KpL2vN",
"schedule": "*/15 * * * *",
"command": "wp cron event run --due-now"
}'
DELETE/cron/<id>/
Removes a cron entry. The change is applied to the site on the next config sync.
curl -X DELETE https://api.koddrio.com/1.0/cron/cron_3xMp8r/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
GET/ssh-keys/
Lists SSH public keys belonging to the current user. Keys grant access to every site the user can reach over SSH.
curl https://api.koddrio.com/1.0/ssh-keys/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
POST/ssh-keys/
Uploads a public key. label is optional — if omitted, the comment portion of the key is used.
curl https://api.koddrio.com/1.0/ssh-keys/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{
"public_key": "ssh-ed25519 AAAAC3Nza... jane@laptop",
"label": "laptop"
}'
DELETE/ssh-keys/<id>/
Removes a public key. SSH sessions already authenticated remain connected until they disconnect.
curl -X DELETE https://api.koddrio.com/1.0/ssh-keys/key_5pNxQ8/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
GET/sites/<id>/ssh-keys/
Lists SSH public keys attached directly to a site. Site-attached keys grant shell access to that one site only — they do not require the holder to have a Koddrio account or organization membership. Useful for granting a customer's developer, a freelance contractor, or a CI runner access to a single site.
curl https://api.koddrio.com/1.0/sites/site_3xRm8KpL2vN/ssh-keys/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
POST/sites/<id>/ssh-keys/
Attaches a public key to a site. The holder of the matching private key can SSH into this site only. Keys are scoped per site — attaching the same key to three sites creates three independent records, and removing one does not affect the others.
curl https://api.koddrio.com/1.0/sites/site_3xRm8KpL2vN/ssh-keys/ \
-H "Authorization: Bearer $KODDRIO_SECRET" \
-H "Content-Type: application/json" \
-d '{
"public_key": "ssh-ed25519 AAAAC3Nza... alice@laptop",
"label": "alice@agency"
}'
DELETE/sites/<id>/ssh-keys/<key_id>/
Removes a site-attached key. Access is revoked immediately for new connections; existing SSH sessions remain connected until they disconnect.
curl -X DELETE https://api.koddrio.com/1.0/sites/site_3xRm8KpL2vN/ssh-keys/sshk_8Yp4q/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
GET/jobs/<id>/
Returns the current state of a job. Only jobs attached to a resource the caller owns are visible.
curl https://api.koddrio.com/1.0/jobs/job_2xMp8rQnKvL/ \
-H "Authorization: Bearer $KODDRIO_SECRET"
{
"id": "job_2xMp8rQnKvL",
"object": "job",
"type": "site.create",
"status": "finished",
"resource": "site:site_3xRm8KpL2vN",
"progress": [
{ "message": "Allocating container", "timestamp": 1712534401 },
{ "message": "Installing WordPress", "timestamp": 1712534413 }
],
"created": 1712534400,
"finished": 1712534429
}
Statuses follow RQ conventions: queued, started, finished, failed, stopped, canceled.
Error codes
| Code | HTTP | Meaning |
|---|---|---|
| parameter_missing | 400 | A required field was not provided |
| invalid_config | 400 | A config value failed validation |
| invalid_request | 400 | The request body could not be parsed |
| unauthorized | 401 | Missing or invalid bearer token |
| permission_denied | 403 | Caller is not a member of the resource's org |
| resource_missing | 404 | No resource with that ID |
| invalid_state | 409 | Resource is not in a state that allows this action |
| no_capacity | 503 | No servers available in the requested region |
Config keys
These keys can be set on site creation or via PATCH /sites/<id>/.
| Key | Default | Allowed values |
|---|---|---|
| php.version | 8.4 | 8.2, 8.3, 8.4 |
| php.workers | 2 | 1 to 64 |
| php.memory | 256M | 64M, 128M, 256M, 512M, 1G |
| php.max_execution_time | 30 | 1 to 90 (seconds) |
| php.upload_max_filesize | 64M | 2M to 1G |
| php.post_max_size | 64M | 2M to 1G |
| database.memory | 256M | 128M to 8G |
| cache.driver | none | none, redis |
| cache.memory | 128M | 64M to 1G |
| storage.size | 10 | 10 to 400 (GB) |
| backups.frequency | daily | hourly, daily, weekly, disabled |
| backups.retention | 30 | 1 to 365 (days) |
| mail.mode | builtin | builtin, relay |
| mail.host | SMTP relay hostname (relay mode) | |
| mail.port | 587 | 1 to 65535 |
| mail.username | SMTP username (relay mode) | |
| mail.from | From address (relay mode) | |
| mail.password | SMTP password (write-only, stored encrypted, never returned) | |
| environment.* | Custom environment variables |
Memory values accept integers (interpreted as MB) or strings with M / G suffixes.
Missing something? Let us know or check the CLI reference for the equivalent commands.