> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sailresearch.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a Sailbox

> Creates a Sailbox and waits for startup to finish, which can take a few minutes. `image` has to describe an image that is ready to boot: a base image on its own, or one already built from that same spec through an SDK.

Send an `Idempotency-Key` so a network retry replays this answer instead of starting a second Sailbox. A server error can still arrive once the Sailbox exists, so read [Retrying safely](/sailboxes-http-api#retrying-safely) before you retry one.



## OpenAPI

````yaml /sailbox-openapi.json post /sailboxes
openapi: 3.1.0
info:
  title: Sail Sailbox API
  version: '2026-08-26'
  description: >-
    Create and operate Sailboxes over HTTP. Sailboxes are fast-booting virtual
    machines that you can pause, resume, and checkpoint.


    Run commands, stream files, manage lifecycle and networking, and inspect
    usage. Turning on SSH and building an image still require an SDK or the CLI.
    See the [HTTP API guide](/sailboxes-http-api).
servers:
  - url: https://sailbox-api.sailresearch.com/v1
security:
  - BearerAuth: []
tags:
  - name: Apps
    description: Groups that own Sailboxes. One app per workload.
  - name: Lifecycle
    description: >-
      A Sailbox's whole life: create it, watch it, stop and start it, terminate
      it.
  - name: Checkpoints
    description: Save a Sailbox to start from later, and copy a running one.
  - name: Exec
    description: Run commands and control their standard input and terminals.
  - name: Files
    description: Stream regular files into and out of a Sailbox.
  - name: Networking
    description: >-
      Publish ports, serve them on hostnames you own, and identify a Sailbox to
      other Sailboxes.
  - name: Secrets
    description: >-
      Organization values that HTTP policies can add to matching requests. Sail
      never returns a stored value.
  - name: HTTP policies
    description: Rules for HTTPS requests a Sailbox sends.
  - name: Volumes
    description: >-
      Shared storage that outlives any one Sailbox. Alpha: these endpoints can
      still change.
  - name: SSH access
    description: Certificates for connecting to a Sailbox with `ssh`.
  - name: Usage
    description: Resource usage and spend.
  - name: Identity
    description: Who your API key belongs to.
paths:
  /sailboxes:
    post:
      tags:
        - Lifecycle
      summary: Create a Sailbox
      description: >-
        Creates a Sailbox and waits for startup to finish, which can take a few
        minutes. `image` has to describe an image that is ready to boot: a base
        image on its own, or one already built from that same spec through an
        SDK.


        Send an `Idempotency-Key` so a network retry replays this answer instead
        of starting a second Sailbox. A server error can still arrive once the
        Sailbox exists, so read [Retrying
        safely](/sailboxes-http-api#retrying-safely) before you retry one.
      operationId: createSailbox
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSailboxRequest'
            example:
              name: worker-1
              app_id: app_0f6a2c31-8b4d-4e7a-9c15-2d8e6f4a1b03
              size: m
              image:
                base: BASE_IMAGE_DEBIAN
              ingress_ports:
                - guest_port: 8000
                  protocol: http
                  allowlist:
                    - 203.0.113.0/24
      responses:
        '200':
          description: >-
            Check `status`: `running` means the Sailbox is ready, and `failed`
            means it did not start, with `error_message` saying why.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreatedSailbox'
              example:
                sailbox_id: sb_9c8f1e2a-3b4d-4f5a-8c7e-1d2f3a4b5c6d
                status: running
          headers:
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
        '400':
          description: >-
            The request was rejected. See the error message for the offending
            field.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: memory_limit_gib for size m must be between 8 and 128
                  type: invalid_request_error
                  param: null
                  code: null
          headers:
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
        '401':
          description: The API key is missing or invalid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: Invalid API key
                  type: authentication_error
                  param: null
                  code: invalid_api_key
        '402':
          description: >-
            The organization is out of credits. The error carries a
            `billing_url` pointing at the page where you add them. Retry once
            you have.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: >-
                    Your API key has been disabled due to insufficient credits.
                    Visit https://app.sailresearch.com/billing to add credits.
                  type: billing_error
                  param: null
                  code: credits_exhausted
                  billing_url: https://app.sailresearch.com/billing
        '403':
          description: The API key is not allowed to perform this operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: sailboxes require an organization-scoped API key
                  type: permission_error
                  param: null
                  code: null
        '404':
          description: >-
            No app with that `app_id` exists in your organization, or a volume
            named in `volume_mounts` does not.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: sailbox "sb_9c8f1e2a-3b4d-4f5a-8c7e-1d2f3a4b5c6d" not found
                  type: not_found_error
                  param: null
                  code: null
          headers:
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
        '409':
          description: >-
            The request cannot be served as written: `image` names an image that
            has not been built, `visibility` is `private` on a key that carries
            no user, or a requested `tcp` port is not available. An
            `Idempotency-Key` reused for a different request lands here too.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: idempotency key reused with a different request body
                  type: conflict_error
                  param: null
                  code: null
          headers:
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
        '413':
          description: The request body is larger than the 256 MiB this endpoint accepts.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: request body too large
                  type: invalid_request_error
                  param: null
                  code: null
        '429':
          description: >-
            Too many requests in flight, or the organization is at a limit. A
            `Retry-After` header means the request never started, so retry it
            unchanged after that delay. Without one, the request ran and hit the
            limit `message` names. Some of those clear on their own, such as a
            shortage of public ports; others need you to free something up
            first. Send the request again once the cause is gone. If you sent an
            `Idempotency-Key`, use a new one, because the old key now replays
            this answer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: >-
                    Too many concurrent requests. Please retry after some of
                    your organization's in-flight requests complete.
                  type: rate_limit_error
                  param: null
                  code: rate_limited
          headers:
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
        '500':
          description: Server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: failed to fetch sailbox
                  type: server_error
                  param: null
                  code: null
        '503':
          description: >-
            Sail is momentarily unavailable. Retry after the delay in the
            `Retry-After` header when one is present, and with backoff
            otherwise.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: Authentication service unavailable
                  type: server_error
                  param: null
                  code: null
          headers:
            Retry-After:
              $ref: '#/components/headers/RetryAfter'
        '504':
          description: >-
            The operation outran its deadline, or a retry sent with the same
            `Idempotency-Key` waited about 30 seconds and the original request
            was still going. The new Sailbox can exist even though the call
            failed, so list your Sailboxes before you retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  message: idempotent request still in flight
                  type: server_error
                  param: null
                  code: null
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        maxLength: 255
      description: >-
        Makes the request retry-safe. Sail remembers the answer it sent under a
        key, including a 400 or a 409, and replays it with `Idempotent-Replayed:
        true` for a retry that repeats the key, the method, the path, and the
        body. Bodies are compared byte for byte. Reusing a key for a different
        request returns 409, so send a corrected request under a new key. A key
        that is blank or only spaces is ignored, and the request runs without
        idempotency.


        Any string of up to 255 bytes works, and characters outside ASCII count
        for more than one. A UUID is a good default. Keys are remembered for at
        least 24 hours and scoped to the API key that sent them.


        A server error, or a failure to record the answer, can leave a key
        unsettled, and creating a Sailbox is where that matters. See [Retrying
        safely](/sailboxes-http-api#retrying-safely).
  schemas:
    CreateSailboxRequest:
      type: object
      required:
        - name
        - app_id
        - image
      properties:
        name:
          type: string
          minLength: 1
          pattern: \S
          maxLength: 128
          description: >-
            Human-readable name for the new Sailbox. Names are not unique, so
            keep hold of the returned `sailbox_id`. It is stored as you send it,
            surrounding spaces included, so trim it yourself if you compare
            names later. Control characters are not allowed.
        app_id:
          type: string
          minLength: 1
          pattern: \S
          description: >-
            Id of the app that will own the Sailbox. Use `POST /apps/find` to
            look one up by name.
        image:
          $ref: '#/components/schemas/ImageSpec'
        size:
          type: string
          enum:
            - s
            - m
            - l
            - ''
          default: m
          description: Machine size. `s` is 1 vCPU, `m` is 4 vCPUs, `l` is 8 vCPUs.
        memory_limit_gib:
          type: integer
          description: >-
            Memory limit in GiB. Allowed ranges are 2 to 64 for size `s`, 8 to
            128 for `m`, and 16 to 256 for `l`. Omit it to take the default for
            the size: 16 for `s`, 32 for `m`, and 64 for `l`.
        state_disk_limit_gib:
          type: integer
          description: >-
            Disk limit in GiB. Allowed ranges are 8 to 128 for size `s`, 32 to
            512 for `m`, and 64 to 1024 for `l`. Omit it to take the default for
            the size: 32 for `s`, 128 for `m`, and 256 for `l`.
        ingress_ports:
          type: array
          items:
            $ref: '#/components/schemas/IngressPort'
          description: >-
            Ports to publish. You can also publish ports later without
            restarting.
        volume_mounts:
          type: array
          items:
            $ref: '#/components/schemas/VolumeMount'
          description: Volumes to mount into the Sailbox.
        visibility:
          type: string
          enum:
            - org
            - private
            - ''
          default: org
          description: >-
            `org` lets anyone in your organization operate the Sailbox.
            `private` restricts that to the user whose key created it, so it
            needs a user-scoped key. An organization admin can still pause,
            resume, sleep, schedule a wake, upgrade, or terminate it by sending
            `X-Sail-Owner-Override-Reason`.
        auto_sleep:
          $ref: '#/components/schemas/AutoSleep'
          description: >-
            Automatic-sleep preference for the new Sailbox. Omit it to let Sail
            sleep the Sailbox automatically with its default wait. You can
            change it later with `POST /sailboxes/{sailbox_id}/auto_sleep`.
        network_policy:
          $ref: '#/components/schemas/NetworkPolicy'
          description: >-
            The new Sailbox's network policy. Omit it for public (unrestricted
            outbound access). Chosen at creation and fixed for the Sailbox's
            life.
      allOf:
        - if:
            required:
              - size
            properties:
              size:
                const: s
          then:
            properties:
              memory_limit_gib:
                anyOf:
                  - const: 0
                  - minimum: 2
                    maximum: 64
              state_disk_limit_gib:
                anyOf:
                  - const: 0
                  - minimum: 8
                    maximum: 128
        - if:
            anyOf:
              - required:
                  - size
                properties:
                  size:
                    enum:
                      - m
                      - ''
              - not:
                  required:
                    - size
          then:
            properties:
              memory_limit_gib:
                anyOf:
                  - const: 0
                  - minimum: 8
                    maximum: 128
              state_disk_limit_gib:
                anyOf:
                  - const: 0
                  - minimum: 32
                    maximum: 512
        - if:
            required:
              - size
            properties:
              size:
                const: l
          then:
            properties:
              memory_limit_gib:
                anyOf:
                  - const: 0
                  - minimum: 16
                    maximum: 256
              state_disk_limit_gib:
                anyOf:
                  - const: 0
                  - minimum: 64
                    maximum: 1024
    CreatedSailbox:
      type: object
      properties:
        sailbox_id:
          type: string
          description: Id of the new Sailbox.
        status:
          type: string
          description: >-
            Current state of the Sailbox. Values in use today are `creating`,
            `running`, `paused`, `sleeping`, `terminating`, `terminated`,
            `failed`, `create_failed`, `interrupted_restorable`, and
            `interrupted_unsafe_to_retry`. New values can appear over time, so
            treat this as an open set.
        error_message:
          type: string
          description: Why the Sailbox failed to start, when it did.
      required:
        - sailbox_id
        - status
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/ErrorObject'
    ImageSpec:
      type: object
      additionalProperties: false
      description: >-
        Describes the environment the Sailbox boots into. Start from a Sail base
        image with `base`, bring your own image from a public registry with
        `oci`, or build your own Dockerfile with `dockerfile`; give exactly one.
        A base image on its own is ready to boot. Adding build steps,
        environment variables, or a Python version describes an image that has
        to be built before a Sailbox can start from it. A registry image or a
        Dockerfile always has to be built first too. Building an image takes an
        SDK.
      oneOf:
        - required:
            - base
        - required:
            - oci
        - required:
            - dockerfile
      properties:
        base:
          type: string
          enum:
            - BASE_IMAGE_DEBIAN
            - BASE_IMAGE_DEVBOX
          description: >-
            Starting point for the image. `BASE_IMAGE_DEBIAN` is a plain Debian
            base you can build on. `BASE_IMAGE_DEVBOX` is a prebuilt development
            environment that accepts no build steps, environment variables, or
            Python version. It includes Docker, with the daemon started
            automatically when the Sailbox boots and kept running across sleeps.
            The daemon can take a few seconds to accept commands right after
            boot. If it stops, it is not restarted automatically.
        oci:
          type: object
          additionalProperties: false
          required:
            - ref
          description: >-
            Your own image as the Sailbox root filesystem. Sail pulls the image
            and layers everything a Sailbox needs on top; build steps and `env`
            compose on it like on a base image. The image must be Debian- or
            Ubuntu-based. Its environment variables, working directory, and
            `USER` become the defaults for commands run in the Sailbox, and its
            `ENTRYPOINT` and `CMD` are not run.
          properties:
            ref:
              type: string
              description: >-
                Image reference on a supported public registry: `docker.io`,
                `ghcr.io`, `public.ecr.aws`, or `quay.io`, written out in full.
                Pass a tag, a `@sha256:...` digest, or just the name, which
                means the `latest` tag. A tag is pinned for your organization
                once an image has been built from it: later builds keep getting
                that version, even if the tag moves upstream. Use `force_build`
                in an SDK to look the tag up again and move the pin for your
                whole organization. A digest names exactly one image, so it
                never moves.
              maxLength: 512
              pattern: >-
                ^[a-z0-9.\-]+(/[a-zA-Z0-9._\-]+)+(:[a-zA-Z0-9._\-]+|@sha256:[0-9a-f]{64})?$
              examples:
                - docker.io/library/python:3.13
        dockerfile:
          type: object
          additionalProperties: false
          required:
            - dockerfile
          description: >-
            Your own Dockerfile built into the Sailbox root filesystem. Sail
            builds it and layers everything a Sailbox needs on top; build steps
            and `env` compose on the result like on a base image. Every image a
            `FROM` or `COPY --from` names must live on a supported public
            registry: `docker.io`, `ghcr.io`, `public.ecr.aws`, or `quay.io`.
            Short names work the way Docker reads them, so `python:3.12` means
            `docker.io/library/python:3.12`. A Dockerfile may name at most 25
            distinct external images across its `FROM` and `COPY --from`
            instructions. The image the Dockerfile produces must be Debian- or
            Ubuntu-based. Its environment variables, working directory, and
            `USER` become the defaults for commands run in the Sailbox, and its
            `ENTRYPOINT` and `CMD` are not run. Multi-stage Dockerfiles work. A
            `RUN --mount` of type `cache`, `secret`, or `ssh` is rejected;
            `tmpfs` mounts work, and `bind` mounts work when they read from the
            build context or another build stage. Mount options must be literal
            text, and `ONBUILD` is not supported, in the Dockerfile or in an
            image a `FROM` names. Each image a `FROM` or `COPY --from` names is
            pinned to the version its tag pointed at on your organization's
            first use, and later builds keep using it after the tag moves; use
            `force_build` in an SDK to look tags up again.
          properties:
            dockerfile:
              type: string
              minLength: 1
              pattern: \S
              maxLength: 524288
              description: >-
                Full text of the Dockerfile. The length limit is in bytes, so
                characters outside ASCII count for more than one.
              examples:
                - |
                  FROM python:3.12
                  RUN pip install requests
            context_files:
              type: array
              maxItems: 10000
              items:
                $ref: '#/components/schemas/AddLocalDirFile'
              description: >-
                The build context: the files the Dockerfile's `COPY` and `ADD`
                instructions read. Their contents are uploaded while the image
                is built. Leave it empty when the Dockerfile copies nothing.
                Every `relative_path` has to be distinct across files,
                directories, and symbolic links, and the three lists together
                may hold at most 10000 entries.
            context_dirs:
              type: array
              maxItems: 10000
              items:
                $ref: '#/components/schemas/DockerfileContextDir'
              description: >-
                The build context's directories, empty ones included. A file
                whose parent directories are not listed still creates them with
                default permissions.
            context_symlinks:
              type: array
              maxItems: 10000
              items:
                $ref: '#/components/schemas/DockerfileContextSymlink'
              description: >-
                The build context's symbolic links. No file, directory, or other
                link may sit at or beneath a link's path.
            pinned_from:
              type: array
              maxItems: 25
              items:
                $ref: '#/components/schemas/DockerfileFromResolution'
              description: >-
                Digest pins carried from a completed build of this Dockerfile,
                as its build reported them. When present, the build uses exactly
                these versions instead of looking the references up, so the spec
                keeps naming the image that build produced even after a forced
                build moves your organization's pins. The set must cover exactly
                the images the Dockerfile names. Leave it out to resolve through
                your organization's pins; a forced build ignores it.
            build_args:
              type: object
              maxProperties: 64
              propertyNames:
                pattern: ^(?!BUILDKIT_)[A-Za-z_][A-Za-z0-9_]*$
                maxLength: 128
              additionalProperties:
                type: string
                maxLength: 4096
                pattern: ^[^\n\r\x00]*$
              description: >-
                Build arguments, matched to the Dockerfile's `ARG` instructions.
                A name must start with a letter or `_` and contain only letters,
                numbers, and `_`; names starting with `BUILDKIT_` are reserved
                for the build system, and Docker's proxy names (`HTTP_PROXY`,
                `HTTPS_PROXY`, `FTP_PROXY`, `NO_PROXY`, `ALL_PROXY`, in any
                letter case) are rejected. Values may not contain newlines or
                NUL. Name and value limits (128 and 4096) are in bytes, so
                characters outside ASCII count for more than one.
        build_steps:
          type: array
          items:
            $ref: '#/components/schemas/ImageBuildStep'
          description: Steps applied to the image source, in order.
        env:
          type: object
          propertyNames:
            pattern: ^[A-Za-z_][A-Za-z0-9_]*$
          additionalProperties:
            type: string
          description: >-
            Environment variables baked into the image and present in every
            process. A variable name must start with a letter or `_` and contain
            only letters, numbers, and `_`.
        architecture:
          type: string
          enum:
            - IMAGE_ARCHITECTURE_AMD64
            - IMAGE_ARCHITECTURE_ARM64
          description: >-
            CPU architecture to build for. With `base` or `dockerfile`, leaving
            this unset builds for `IMAGE_ARCHITECTURE_AMD64`. With `oci`,
            leaving it unset builds for whichever architecture the registry
            image was built for, and for `IMAGE_ARCHITECTURE_AMD64` when it was
            built for both. Naming one makes it a requirement: a registry image
            that was not built for it is rejected.
        python_version:
          type: string
          pattern: ^([0-9]+\.[0-9]+\.[0-9]+)?$
          description: >-
            Python version to install as `python3`. Give the full patch version,
            such as `3.12.13`; `3.12` is rejected. Not accepted with `oci` or
            `dockerfile`: a pinned interpreter would shadow the Python the image
            was built around.
          examples:
            - 3.12.13
      allOf:
        - if:
            required:
              - base
            properties:
              base:
                const: BASE_IMAGE_DEVBOX
          then:
            properties:
              build_steps:
                maxItems: 0
              env:
                maxProperties: 0
              python_version:
                maxLength: 0
        - if:
            required:
              - oci
          then:
            properties:
              python_version:
                maxLength: 0
        - if:
            required:
              - dockerfile
          then:
            properties:
              python_version:
                maxLength: 0
    IngressPort:
      type: object
      required:
        - guest_port
      properties:
        guest_port:
          type: integer
          minimum: 1
          maximum: 65535
          description: >-
            Port your process listens on inside the Sailbox. Ports 10000, 10001,
            15001, and 15002 are reserved and cannot be published on either
            protocol. Port 22 is reserved on `http` and available on `tcp`,
            which is what SSH needs.


            A guest port is published under one protocol at a time. Publishing
            it again under that same protocol replaces its allowlist; publishing
            it under the other protocol is refused.
        protocol:
          type: string
          enum:
            - http
            - tcp
            - ''
          default: http
          description: >-
            `http` publishes an HTTPS URL. `tcp` publishes a host and port for
            raw TCP, which is what SSH and database clients need.
        allowlist:
          type: array
          items:
            type: string
          description: >-
            Restricts who can reach the listener. An address or a range, such as
            `203.0.113.0/24`, lets matching source IPs in, and works on both
            protocols. An app name, which lets Sailboxes in that app call this
            one, works on `http` only and is rejected on `tcp`.


            An app name must not look like an address or a range, and must not
            contain a `/`. Every entry that reads as an address or a range is
            taken as one, and any other entry holding a `/` is refused as a
            malformed range. An address must not carry an IPv6 zone, such as
            `fe80::1%eth0`, which names an interface on one machine rather than
            a source.


            Entries come back canonicalized: a range is reported by its network
            address, a bare address as a single-address range, and blank or
            repeated entries are dropped.


            Leaving it empty makes the listener reachable by anyone who has the
            address. That is refused on a `tcp` listener for a well-known
            unauthenticated service port such as 5432 or 6379: publish those
            with an explicit allowlist, using `0.0.0.0/0` and `::/0` if you do
            want the whole internet.
    VolumeMount:
      type: object
      required:
        - volume_id
        - mount_path
      properties:
        volume_id:
          type: string
          minLength: 1
          pattern: \S
          description: Id of a volume you created earlier.
        mount_path:
          type: string
          pattern: ^\s*/.
          description: >-
            Absolute path inside the Sailbox where the volume appears. Repeated
            slashes and `.`/`..` segments are resolved first, and the result
            cannot be `/`. Two mounts in one request cannot nest inside each
            other.
          examples:
            - /mnt/shared
    AutoSleep:
      type: object
      description: >-
        A Sailbox's automatic-sleep preference, in both directions: send it to
        choose, and read it back to see the choice in effect. An explicit idle
        window replaces Sail's default and can make automatic sleep happen
        sooner or later. The window only controls when Sail may consider
        sleeping the Sailbox; it still sleeps only when fully idle. Your own
        `sleep`, `pause`, `resume`, and scheduled wakes work the same whatever
        it says.
      properties:
        automatic:
          type: boolean
          default: true
          description: >-
            `true` lets Sail sleep the Sailbox automatically when that is safe,
            which is the default. `false` turns automatic sleeping off. Omitting
            it when you write means `true`. Reads always include it.
        min_seconds_before_sleep:
          type: integer
          minimum: 0
          maximum: 3600
          description: >-
            The idle window to use instead of Sail's default. Whole-second
            values from 1 through 3600 are accepted; 0 restores Sail's default,
            and other numeric values are rejected. Once the window passes, Sail
            may sleep the Sailbox only when it is fully idle. Send it only with
            `automatic: true`; sent alongside `automatic: false` the request is
            rejected, because a caller who meant to set a wait would otherwise
            have silently turned automatic sleeping off. Reads leave it out when
            Sail's default is in effect.
          examples:
            - 30
            - 300
    NetworkPolicy:
      type: object
      description: >-
        A Sailbox's network policy, chosen at creation and fixed for its life. A
        Sailbox created from a checkpoint keeps the policy of the one it came
        from. `allowed_hosts` lists at least one destination with the
        `allowlist` mode; with any other mode it must be empty or omitted.
      properties:
        mode:
          type: string
          enum:
            - public
            - ''
            - no_network
            - allowlist
          description: >-
            `"public"` (or the empty string / omitting the policy) is public:
            unrestricted network access, the default. `"no_network"` cuts the
            Sailbox off from other hosts and the internet, so it cannot make
            outbound connections or expose inbound services and name resolution
            does not work. Running commands is unaffected (exec reaches the
            Sailbox over a Sail-internal path, not its network); mounted volumes
            and other platform features it was created with keep working.
            `"allowlist"` allows only connections to the destinations in
            `allowed_hosts`. It applies only to connections the Sailbox opens,
            so `ingress_ports` and SSH still work.
        allowed_hosts:
          type: array
          items:
            type: string
          maxItems: 128
          description: >-
            Allowlist destinations, set only with `mode: "allowlist"` and then
            at least one entry, at most 128. Each entry is a hostname, a `*.`
            wildcard hostname (one extra name part), an IPv4 address, or an IPv4
            range in CIDR form such as `203.0.113.0/24`. IPv6 entries are
            rejected. Entry rules and what each entry allows:
            https://docs.sailresearch.com/sailboxes-network-policy
      allOf:
        - if:
            required:
              - mode
            properties:
              mode:
                const: allowlist
          then:
            required:
              - allowed_hosts
            properties:
              allowed_hosts:
                minItems: 1
          else:
            properties:
              allowed_hosts:
                maxItems: 0
    ErrorObject:
      type: object
      required:
        - message
        - type
        - param
        - code
      properties:
        message:
          type: string
          description: Human-readable description of what went wrong.
        type:
          type: string
          description: >-
            Error category: `invalid_request_error`, `authentication_error`,
            `permission_error`, `billing_error`, `not_found_error`,
            `conflict_error`, `rate_limit_error`, `quota_exceeded_error`, or
            `server_error`.
        param:
          oneOf:
            - type: string
            - type: 'null'
          description: >-
            The request field the error refers to, when the error is about one
            field.
        code:
          oneOf:
            - type: string
            - type: integer
            - type: 'null'
          description: >-
            Stable short code for the error, when one applies. Errors relayed
            from the Sailbox runtime carry its status token, for example
            `already_exists` or `failed_precondition` on a 409, which the HTTP
            status alone does not distinguish.
        billing_url:
          type: string
          format: uri
          description: Page where you add credits. Sent with a 402.
      additionalProperties: true
    AddLocalDirFile:
      type: object
      additionalProperties: false
      description: One file within an `add_local_dir` step or a `dockerfile` build context.
      required:
        - relative_path
        - content_sha256
      properties:
        relative_path:
          type: string
          maxLength: 1024
          pattern: ^[^/]
          description: >-
            Path within the copied directory or build context. No leading or
            trailing `/`, and no empty, `.`, or `..` segment. The limit is 1024
            bytes, and characters outside ASCII count for more than one.
          examples:
            - src/main.py
        content_sha256:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: SHA-256 of the file contents, lowercase hex.
        mode:
          type: integer
          minimum: 0
          maximum: 511
          description: >-
            Permission bits for the file, as a number. `493` is `rwxr-xr-x`.
            Omit it, or send `0`, for `420` (`rw-r--r--`).
    DockerfileContextDir:
      type: object
      additionalProperties: false
      description: >-
        One directory within a `dockerfile` build context. Send one for every
        directory the context contains, including empty ones, so `COPY` of a
        directory behaves the way it does locally.
      required:
        - relative_path
      properties:
        relative_path:
          type: string
          maxLength: 1024
          pattern: ^[^/]
          description: >-
            Path of the directory within the build context. The same rules as a
            context file's `relative_path`.
          examples:
            - src
        mode:
          type: integer
          minimum: 0
          maximum: 511
          description: >-
            Permission bits for the directory, as a number. Omit it, or send
            `0`, for `493` (`rwxr-xr-x`).
    DockerfileContextSymlink:
      type: object
      additionalProperties: false
      description: >-
        One symbolic link within a `dockerfile` build context. The link is
        recreated as a link in the staged context, exactly as `docker build`
        sends one.
      required:
        - relative_path
        - target
      properties:
        relative_path:
          type: string
          maxLength: 1024
          pattern: ^[^/]
          description: >-
            Path of the link within the build context. The same rules as a
            context file's `relative_path`. No other context entry may sit at or
            beneath this path.
          examples:
            - current
        target:
          type: string
          minLength: 1
          maxLength: 1024
          description: >-
            The link's target, exactly as the link stores it. It may be relative
            or absolute and is resolved only inside the built image, never while
            staging the context.
          examples:
            - releases/v2
    DockerfileFromResolution:
      type: object
      additionalProperties: false
      description: >-
        What one image named by a Dockerfile's `FROM` or `COPY --from` resolved
        to when an image was built.
      required:
        - reference
        - digest_ref
      properties:
        reference:
          type: string
          maxLength: 512
          pattern: >-
            ^[a-z0-9.\-]+(/[a-zA-Z0-9._\-]+)+(:[a-zA-Z0-9._\-]+|@sha256:[0-9a-f]{64})?$
          description: The image reference as the Dockerfile names it, fully qualified.
          examples:
            - docker.io/library/python:3.12
        digest_ref:
          type: string
          maxLength: 512
          pattern: ^[a-z0-9.\-]+(/[a-zA-Z0-9._\-]+)+@sha256:[0-9a-f]{64}$
          description: >-
            The digest-pinned form of the same reference the build used. It must
            be in the reference's own repository.
          examples:
            - >-
              docker.io/library/python@sha256:0000000000000000000000000000000000000000000000000000000000000000
    ImageBuildStep:
      type: object
      additionalProperties: false
      description: One build step. Set exactly one field per step. Steps run in order.
      minProperties: 1
      maxProperties: 1
      properties:
        apt_install:
          $ref: '#/components/schemas/PackageInstall'
        pip_install:
          $ref: '#/components/schemas/PackageInstall'
        run_command:
          type: object
          additionalProperties: false
          required:
            - command
          properties:
            command:
              type: string
              minLength: 1
              pattern: \S
              description: Shell command to run while building the image.
        add_local_file:
          $ref: '#/components/schemas/AddLocalFile'
        add_local_dir:
          $ref: '#/components/schemas/AddLocalDir'
    PackageInstall:
      type: object
      additionalProperties: false
      required:
        - packages
      properties:
        packages:
          type: array
          minItems: 1
          items:
            type: string
            minLength: 1
            pattern: \S
          description: Package names to install.
    AddLocalFile:
      type: object
      additionalProperties: false
      description: >-
        Copies one file into the image. Its contents are uploaded while the
        image is built.
      required:
        - content_sha256
        - remote_path
      properties:
        content_sha256:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: SHA-256 of the file contents, lowercase hex.
        remote_path:
          type: string
          pattern: ^/
          description: >-
            Absolute path the file lands on inside the image. No trailing `/`
            and no `..` segment. A space, `$`, `"`, or `\` anywhere in the path
            is rejected.
          examples:
            - /opt/app/entrypoint.sh
        mode:
          type: integer
          minimum: 0
          maximum: 511
          description: >-
            Permission bits for the file, as a number. `493` is `rwxr-xr-x`.
            Omit it, or send `0`, for `420` (`rw-r--r--`).
    AddLocalDir:
      type: object
      additionalProperties: false
      description: >-
        Copies a directory of files into the image. Their contents are uploaded
        while the image is built.
      required:
        - remote_path
        - files
      properties:
        remote_path:
          type: string
          pattern: ^/
          description: >-
            Absolute directory the files land in inside the image. It cannot be
            `/`. No trailing `/` and no `..` segment. A space, `$`, `"`, or `\`
            anywhere in the path is rejected.
          examples:
            - /opt/app
        files:
          type: array
          minItems: 1
          maxItems: 50000
          items:
            $ref: '#/components/schemas/AddLocalDirFile'
          description: The files to copy. Every `relative_path` has to be distinct.
  headers:
    IdempotentReplayed:
      description: >-
        `true` when this response is the stored answer to an earlier request
        that sent the same `Idempotency-Key`. Absent when the request ran for
        the first time.
      schema:
        type: string
        enum:
          - 'true'
    RetryAfter:
      description: >-
        How many seconds to wait before retrying. On a 429 it also means the
        request never started, so the retry can reuse the same
        `Idempotency-Key`.
      schema:
        type: integer
        minimum: 0
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key

````