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:

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_missing400A required field was not provided
invalid_config400A config value failed validation
invalid_request400The request body could not be parsed
unauthorized401Missing or invalid bearer token
permission_denied403Caller is not a member of the resource's org
resource_missing404No resource with that ID
invalid_state409Resource is not in a state that allows this action
no_capacity503No 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.version8.48.2, 8.3, 8.4
php.workers21 to 64
php.memory256M64M, 128M, 256M, 512M, 1G
php.max_execution_time301 to 90 (seconds)
php.upload_max_filesize64M2M to 1G
php.post_max_size64M2M to 1G
database.memory256M128M to 8G
cache.drivernonenone, redis
cache.memory128M64M to 1G
storage.size1010 to 400 (GB)
backups.frequencydailyhourly, daily, weekly, disabled
backups.retention301 to 365 (days)
mail.modebuiltinbuiltin, relay
mail.hostSMTP relay hostname (relay mode)
mail.port5871 to 65535
mail.usernameSMTP username (relay mode)
mail.fromFrom address (relay mode)
mail.passwordSMTP 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.