Sign Up

Images

GET
/cache/images
  • "is precached?" ?ref=ubuntu:22.04 → the image + per-layer hit/miss
  • "what's cached?" (no ref) → a paginated list of cached ref-manifests

The API has no NFS mount (control-plane / runner split), so the scan runs on a runner over NATS request/reply (runner.<id>.cache.images). The cache is shared across all runners' NFS, so ANY live runner yields the same answer; we try live runners in turn until one replies. This is deliberately NOT scoped to the caller's project: the CAS is global infrastructure keyed by layer diff-id. Per-project image ownership lives in GET /images, a separate plane.

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Query Parameters

ref?string

When set, a single-ref precache lookup with per-layer detail (e.g. ?ref=ubuntu:22.04). Otherwise a paginated list.

limit?integer

Page size for the list (clamped runner-side to [1, 500]); ignored for ref.

Formatint64
cursor?string

Opaque continuation token from a previous page's next_cursor.

Response Body

Runner cache scan result (proxied runner reply, shape is runner-defined): with ref, the image + per-layer hit/miss; without, a paginated { images, next_cursor } list

response?unknownapplication/json
curl -X GET 'https://example.com/cache/images'
null

GET /images

GET
/images

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Scope: read:images

Query Parameters

limit?integer
Formatint64
cursor?string
name?string
sandbox_id?string
anonymous?string
platform?string

Which images to list: false (default) the project's own, true the platform images (deprecated ones excluded), any both.

Response Body

Images in the project and, with platform=true|any, the platform images (keyset-paginated, newest first). The anonymous backup image a v1 sandbox import registers (imported_from, no name) is never listed — it only serves the sandbox it was imported for and stays reachable by id

application/json
  1. response

Page of images (GET /images).

images*array<>
next_cursor?|

Opaque keyset token for the next page; null on the last page.

curl -X GET 'https://example.com/images'
{  "images": [    {      "anonymous": true,      "available_in": [        "string"      ],      "bytes": 0,      "class": "string",      "default": true,      "deprecated": true,      "error": "string",      "expires_at": "2019-08-24T14:15:22Z",      "format_outdated": true,      "format_version": 0,      "id": "string",      "imported_from": {        "id": "string",        "platform": "string"      },      "layer_count": 0,      "manifest_name": "string",      "name": "string",      "origin": "string",      "platform": true,      "region": "string",      "retention": "string",      "source": null,      "status": "string",      "created_at": "2019-08-24T14:15:22Z",      "cursor": "string"    }  ],  "next_cursor": "string"}
POST
/images

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Scope: write:images

Request Body

JSON build request (registry/named source). Alternatively send multipart/form-data with fields name, optional dockerfile (path within the context), region, cpu_arch, and the file field context (a build-context tar).

  1. body
cpu_arch?|

CPU architecture to build for — amd64 or arm64. Absent => the region's default (PLAN-ARM64.md §2.2).

The same Dockerfile produces a different image per architecture, so this is part of the build's content key: an amd64 and an arm64 build of one Dockerfile are two builds that never adopt each other (PLAN-BUILD-DEDUPE.md). It also pins which runners may serve the build.

dockerfile?|

An inline, self-contained Dockerfile body to build (no COPY/ADD of local files — use the multipart/form-data variant to upload a real build context). The API synthesizes the trivial single-file context for you. Mutually exclusive with source.

idle?|

Idle-retention window in seconds for an ANONYMOUS image: the image is deleted once this long has passed without a sandbox being created from it (every create from the image restarts the clock). Only without name (a named image is permanent; idle with name is 400). Accepted: omitted or -1 ⇒ the server maximum (DAYTONA_ANON_IMAGE_MAX_IDLE, 7 days by default); 1 … max ⇒ that window. 0, values below -1, and values above the maximum ⇒ 400 IDLE_OUT_OF_RANGE (the body carries max).

Formatint64
name?string|null
region?|

Target region for the build (PLAN-REGIONS.md). Absent => resolved default. The image is homed in this region; other regions replicate it on first use.

source?null||||

Response Body

Build enqueued

application/json
  1. response

Response for POST /images: the enqueued build. Field order is alphabetical to keep the wire bytes identical to the former serde_json::json! construction (BTreeMap key order).

image_id*string
job_id?|

The build's CURRENT attempt. For an adopted image this is the job that already produced it, not a job this call enqueued — so it is not evidence that a build was started. null when the image was reused and no build job is on record for it (an anonymous registry import whose pull job has been pruned): poll GET /images/{id} for status instead — the image id, not a job id, is the handle to wait on.

Formatuuid
name?string|null
region*string

Home region the image is built in (PLAN-REGIONS.md).

status*string

The build's status. pending for a build this call enqueued, and building when it joined one already in flight — but ready when the image was ADOPTED from an identical build that had already finished (PLAN-BUILD-DEDUPE.md), in which case no build runs and the image is usable immediately. Poll GET /images (or the job) for progress.

curl -X POST 'https://example.com/images' \  -H 'Content-Type: application/json' \  -d '{}'
{  "image_id": "string",  "job_id": "453bd7d7-5355-4d6d-a38e-d9e7eb218c3f",  "name": "string",  "region": "string",  "status": "string"}
GET
/images/{id}

Mirrors GET /snapshots/{id}: a build is asynchronous, so a client that just POSTed needs somewhere to wait. (Before PLAN-ARTIFACT-UNIFICATION.md DP-7 the only such surface was the Daytona alias facade, which polled the same row through a second table.)

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Scope: read:images

Path Parameters

id*string

image id (img_…; bare UUID also accepted during the transition)

Query Parameters

wait?string

Block until the image reaches this status (ready) or a terminal state (ready/failed). Absent => return immediately.

timeout_ms?integer
Formatint64
Range0 <= value

Response Body

The image

application/json
  1. response
anonymous*boolean
available_in*array<string>

Every region that holds a copy of this image's current manifest right now (PLAN-ARTIFACT-RESIDENCY.md "The copy record"); the home region is listed only while its own copy is there. Empty until the build finishes, and after every copy has been evicted. A create in a region not listed here waits while that region fetches the image from the global artifact store. A registry import whose export is not yet durable, or a create of one that carries volumes, is pulled from the registry again instead; any other create that carries volumes is refused there.

Not a column: filled in by one batched query per page.

bytes?|

Captured erofs layer size in bytes; null until the build completes.

Formatint64
class*string
created_at*string
Formatdate-time
default*boolean

The platform default image: what POST /sandboxes without image boots. Only ever true on a platform image.

deprecated*boolean

A deprecated platform image: no new creates by name, not listed by GET /images?platform=…; its img_… id still resolves.

error?|

Build failure reason; null unless status is failed.

expires_at?|
Formatdate-time
format_outdated*boolean

format_version is below the current format. Such an image converts on its first Linux sandbox create (which waits queued meanwhile) or ahead of time with POST /images/{id}/convert; every other door (GPU, Windows, functions, a POST /images named source) refuses it with 409 IMAGE_FORMAT_OUTDATED until then. Derived, never stored.

format_version*integer

The image's format generation — how its artifact is stored (images::format): 1 = stored by Daytona v1 (the v1 → v2 migration registers these; nothing pulled yet), 2 = the platform's current artifact. status is the pipeline state of the artifact at this format.

Formatint32
id*string
imported_from?

Where the image was imported from (an archived Daytona v1 sandbox's backup, #3035): {platform: "v1", id: <v1 sandbox id>}. Absent (never null) when the image was not imported, so the item and the page row share one optional, non-nullable shape.

layer_count?|
Formatint32
manifest_name?|

Resolved erofs manifest name; null until the build completes.

name?string|null
origin*string
platform*boolean

A platform image: published by the platform operator, owned by no project, and bootable by every project (PLAN-PLATFORM-IMAGES.md). A project's own image of the same name takes precedence for that project. Platform images cannot be renamed, converted or deleted through this API.

region*string

Home region the image was built in (PLAN-REGIONS.md).

retention*string
source?unknown

The build source as stored (ImageSource JSON).

status*string
curl -X GET 'https://example.com/images/string'
{  "anonymous": true,  "available_in": [    "string"  ],  "bytes": 0,  "class": "string",  "created_at": "2019-08-24T14:15:22Z",  "default": true,  "deprecated": true,  "error": "string",  "expires_at": "2019-08-24T14:15:22Z",  "format_outdated": true,  "format_version": 0,  "id": "string",  "imported_from": {    "id": "string",    "platform": "string"  },  "layer_count": 0,  "manifest_name": "string",  "name": "string",  "origin": "string",  "platform": true,  "region": "string",  "retention": "string",  "source": null,  "status": "string"}
PATCH
/images/{id}

Promotion only: an image that already has a name answers 409. Renaming a named image is deliberately not offered yet — sandboxes reference images by id, so a rename would silently relabel every sandbox created from it, and nothing would record the name the caller created them with.

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Scope: write:images

Path Parameters

id*string

image id (img_…; bare UUID also accepted during the transition)

Request Body

application/json
  1. body
name*string

Response Body

The now-named, permanent image

application/json
  1. response
anonymous*boolean
available_in*array<string>

Every region that holds a copy of this image's current manifest right now (PLAN-ARTIFACT-RESIDENCY.md "The copy record"); the home region is listed only while its own copy is there. Empty until the build finishes, and after every copy has been evicted. A create in a region not listed here waits while that region fetches the image from the global artifact store. A registry import whose export is not yet durable, or a create of one that carries volumes, is pulled from the registry again instead; any other create that carries volumes is refused there.

Not a column: filled in by one batched query per page.

bytes?|

Captured erofs layer size in bytes; null until the build completes.

Formatint64
class*string
created_at*string
Formatdate-time
default*boolean

The platform default image: what POST /sandboxes without image boots. Only ever true on a platform image.

deprecated*boolean

A deprecated platform image: no new creates by name, not listed by GET /images?platform=…; its img_… id still resolves.

error?|

Build failure reason; null unless status is failed.

expires_at?|
Formatdate-time
format_outdated*boolean

format_version is below the current format. Such an image converts on its first Linux sandbox create (which waits queued meanwhile) or ahead of time with POST /images/{id}/convert; every other door (GPU, Windows, functions, a POST /images named source) refuses it with 409 IMAGE_FORMAT_OUTDATED until then. Derived, never stored.

format_version*integer

The image's format generation — how its artifact is stored (images::format): 1 = stored by Daytona v1 (the v1 → v2 migration registers these; nothing pulled yet), 2 = the platform's current artifact. status is the pipeline state of the artifact at this format.

Formatint32
id*string
imported_from?

Where the image was imported from (an archived Daytona v1 sandbox's backup, #3035): {platform: "v1", id: <v1 sandbox id>}. Absent (never null) when the image was not imported, so the item and the page row share one optional, non-nullable shape.

layer_count?|
Formatint32
manifest_name?|

Resolved erofs manifest name; null until the build completes.

name?string|null
origin*string
platform*boolean

A platform image: published by the platform operator, owned by no project, and bootable by every project (PLAN-PLATFORM-IMAGES.md). A project's own image of the same name takes precedence for that project. Platform images cannot be renamed, converted or deleted through this API.

region*string

Home region the image was built in (PLAN-REGIONS.md).

retention*string
source?unknown

The build source as stored (ImageSource JSON).

status*string
curl -X PATCH 'https://example.com/images/string' \  -H 'Content-Type: application/json' \  -d '{  "name": "string"}'
{  "anonymous": true,  "available_in": [    "string"  ],  "bytes": 0,  "class": "string",  "created_at": "2019-08-24T14:15:22Z",  "default": true,  "deprecated": true,  "error": "string",  "expires_at": "2019-08-24T14:15:22Z",  "format_outdated": true,  "format_version": 0,  "id": "string",  "imported_from": {    "id": "string",    "platform": "string"  },  "layer_count": 0,  "manifest_name": "string",  "name": "string",  "origin": "string",  "platform": true,  "region": "string",  "retention": "string",  "source": null,  "status": "string"}
DELETE
/images/{id}

New in PLAN-ARTIFACT-UNIFICATION.md phase 2. Before it, image deletion existed ONLY inside the Daytona alias facade, running a shorter guard set than the snapshot route — so which endpoint you happened to use decided whether a paused sandbox kept its layers. Both routes are the same code now (snapshots::delete_artifact), and neither can reach across the image/snapshot partition.

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Scope: delete:images

Path Parameters

id*string

image id (img_…; bare UUID also accepted during the transition)

Response Body

Image deleted

application/json
  1. response

DELETE /images/:id response.

image_id*string
status*string

Always deleted.

curl -X DELETE 'https://example.com/images/string'
{  "image_id": "string",  "status": "string"}
GET
/images/{id}/build-logs-url

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Scope: read:images

Path Parameters

id*string

image id (img_…; bare UUID also accepted during the transition)

Response Body

Absolute regional proxy URL

application/json
  1. response

Absolute canonical regional proxy URL for once, text-follow and SSE reads.

url*string
curl -X GET 'https://example.com/images/string/build-logs-url'
{  "url": "string"}
POST
/images/{id}/convert

The same conversion the first create runs (images::format::convert): an image below the current format (format_outdated) flips to pending and its conversion job — for a format-1 image, the registry import of its v1 ref — is enqueued in the image's region. Idempotent and safe to repeat: a conversion already in flight is joined (nothing enqueued), a failed one is retried, and an image already at the current format answers converted: false with its own status, whatever that is — its build is POST /images' business.

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Scope: write:images

Path Parameters

id*string

image id (img_…; bare UUID also accepted during the transition)

Response Body

The conversion started or joined (converted: true), or nothing to convert (converted: false)

application/json
  1. response

POST /images/{id}/convert (#3032): the result.

converted*boolean

true when this call started or joined a conversion; false when the image was already at the current format and nothing was enqueued.

from_format*integer

The image's format before the call (ImageListItem.format_version).

Formatint32
image_id*string
status*string

The image's status after the call: pending/building while it converts, or its own status when it was already at the current format.

to_format*integer

The format the conversion targets: the current one.

Formatint32
curl -X POST 'https://example.com/images/string/convert'
{  "converted": true,  "from_format": 0,  "image_id": "string",  "status": "string",  "to_format": 0}
POST
/sandboxes/{id}/images

Authorization

headerAuthorizationBearer <token>

Project API key (dtn_…) as a Bearer token. Mint via the management API (POST /projects/:id/api-keys) or this deployment's key-mint CLI. Every key carries an explicit set of verb:resource project permissions chosen at mint time (PLAN-API-KEY-PERMISSIONS.md); each operation lists the project permission it requires in its security requirement, and a key lacking it is answered 403 missing required project permission: <project-permission>. Some operations require an additional permission depending on the request body, stated in the operation's description. Holding a mutating verb (write:/delete:/invoke:) implies the resource's read: permission. Operations whose api_key project-permission list is empty need no specific project permission — any live project key works (the global catalogs and the artifact-cache read).

Scope: write:images

Path Parameters

id*string

external sandbox id (base58 body with optional region tag, or a bare UUID)

Request Body

application/json
  1. body
idle?|

Idle-retention window in seconds for an ANONYMOUS image: the image is deleted once this long has passed without a sandbox being created from it (every create from the image restarts the clock). Only without name (a named image is permanent; idle with name is 400). The same rule as POST /images: omitted or -1 ⇒ the server maximum (DAYTONA_ANON_IMAGE_MAX_IDLE, 7 days by default); 1 … max ⇒ that window; 0, < -1 or > max ⇒ 400 IDLE_OUT_OF_RANGE (the body carries max). Promote the image (PATCH /images/{id}) to keep it.

Formatint64
name?string|null

Response Body

Filesystem capture accepted; image row queued on the sandbox's runner

application/json
  1. response
anonymous*boolean
available_in*array<string>

Every region that holds a copy of this image's current manifest right now (PLAN-ARTIFACT-RESIDENCY.md "The copy record"); the home region is listed only while its own copy is there. Empty until the build finishes, and after every copy has been evicted. A create in a region not listed here waits while that region fetches the image from the global artifact store. A registry import whose export is not yet durable, or a create of one that carries volumes, is pulled from the registry again instead; any other create that carries volumes is refused there.

Not a column: filled in by one batched query per page.

bytes?|

Captured erofs layer size in bytes; null until the build completes.

Formatint64
class*string
created_at*string
Formatdate-time
default*boolean

The platform default image: what POST /sandboxes without image boots. Only ever true on a platform image.

deprecated*boolean

A deprecated platform image: no new creates by name, not listed by GET /images?platform=…; its img_… id still resolves.

error?|

Build failure reason; null unless status is failed.

expires_at?|
Formatdate-time
format_outdated*boolean

format_version is below the current format. Such an image converts on its first Linux sandbox create (which waits queued meanwhile) or ahead of time with POST /images/{id}/convert; every other door (GPU, Windows, functions, a POST /images named source) refuses it with 409 IMAGE_FORMAT_OUTDATED until then. Derived, never stored.

format_version*integer

The image's format generation — how its artifact is stored (images::format): 1 = stored by Daytona v1 (the v1 → v2 migration registers these; nothing pulled yet), 2 = the platform's current artifact. status is the pipeline state of the artifact at this format.

Formatint32
id*string
imported_from?

Where the image was imported from (an archived Daytona v1 sandbox's backup, #3035): {platform: "v1", id: <v1 sandbox id>}. Absent (never null) when the image was not imported, so the item and the page row share one optional, non-nullable shape.

layer_count?|
Formatint32
manifest_name?|

Resolved erofs manifest name; null until the build completes.

name?string|null
origin*string
platform*boolean

A platform image: published by the platform operator, owned by no project, and bootable by every project (PLAN-PLATFORM-IMAGES.md). A project's own image of the same name takes precedence for that project. Platform images cannot be renamed, converted or deleted through this API.

region*string

Home region the image was built in (PLAN-REGIONS.md).

retention*string
source?unknown

The build source as stored (ImageSource JSON).

status*string
curl -X POST 'https://example.com/sandboxes/string/images' \  -H 'Content-Type: application/json' \  -d '{}'
{  "anonymous": true,  "available_in": [    "string"  ],  "bytes": 0,  "class": "string",  "created_at": "2019-08-24T14:15:22Z",  "default": true,  "deprecated": true,  "error": "string",  "expires_at": "2019-08-24T14:15:22Z",  "format_outdated": true,  "format_version": 0,  "id": "string",  "imported_from": {    "id": "string",    "platform": "string"  },  "layer_count": 0,  "manifest_name": "string",  "name": "string",  "origin": "string",  "platform": true,  "region": "string",  "retention": "string",  "source": null,  "status": "string"}