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

# Update an instance pool's description, size, tags or launch template

> Change description, desired_count, min_count, max_count, the pool's own tags, and/or
the launch template. Omitted bounds retain their current values.
The resulting bounds must satisfy 0 <= min_count <= max_count <= 100.
If desired_count is omitted, it is clamped into the new bounds and the
reconciler scales the live instance set to match. An explicit desired_count
must lie within the new bounds. Invalid sizing returns 400 with nothing
applied, including tags and template changes. Managed pools remain read-only
through the customer API.

Lowering max_count to desired_count removes refresh surge headroom.
The refresh waits until headroom becomes available; normal scaling continues.

`description` changes only the pool's note. Omit it to preserve the note,
or send an empty string to clear it. This never rolls or resizes instances.

`tags` relabels the POOL and nothing else: it takes effect immediately,
no instance is touched, and the new set is what a later IAM condition
reads as `basalt:ResourceTag/<key>`. It replaces the whole set — an empty
object clears it, an omitted field leaves it alone.

A new `template` replaces the stored one wholesale and changes what the
pool launches NEXT; the instances already running keep what they booted
with, because a live VM cannot change flavor, tier, subnet or tags in
place. So a `template.tags` edit leaves the pool holding members with two
different tag sets until it is rolled. The pool reports
`stale_instance_count` — how many members are on the old template — and
POST /v1/instance-pools/{pool_id}/refresh rolls them.


<Info>
  Requires the IAM action **`compute:UpdateInstancePool`**. See [COMPUTE permissions](/compute/permissions) for the full list, what each one covers, and an example policy.
</Info>


## OpenAPI

````yaml /api-reference/specs/compute.yaml patch /v1/instance-pools/{pool_id}
openapi: 3.0.3
info:
  title: Basaltic Compute API
  version: 1.0.0
  description: |
    Virtual machine instances, and the images, flavors, SSH keypairs and
    instance pools they are built from. Covers the whole instance lifecycle:
    start, stop, reboot, resize and reinstall.
  contact:
    name: Basaltic Support
    email: ping@basaltic.sh
  license:
    name: Proprietary
    url: https://basaltic.sh/terms
servers:
  - url: https://compute.{region}.basaltic.sh
    description: Regional API endpoint
    variables:
      region:
        default: sa-saopaulo-1
        description: Region code
security:
  - BearerAuth: []
paths:
  /v1/instance-pools/{pool_id}:
    parameters:
      - name: pool_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
          example: 8f2a1c3d-4e5b-4a6f-9c0d-1e2f3a4b5c6d
    patch:
      tags:
        - Compute
      summary: Update an instance pool's description, size, tags or launch template
      description: >
        Change description, desired_count, min_count, max_count, the pool's own
        tags, and/or

        the launch template. Omitted bounds retain their current values.

        The resulting bounds must satisfy 0 <= min_count <= max_count <= 100.

        If desired_count is omitted, it is clamped into the new bounds and the

        reconciler scales the live instance set to match. An explicit
        desired_count

        must lie within the new bounds. Invalid sizing returns 400 with nothing

        applied, including tags and template changes. Managed pools remain
        read-only

        through the customer API.


        Lowering max_count to desired_count removes refresh surge headroom.

        The refresh waits until headroom becomes available; normal scaling
        continues.


        `description` changes only the pool's note. Omit it to preserve the
        note,

        or send an empty string to clear it. This never rolls or resizes
        instances.


        `tags` relabels the POOL and nothing else: it takes effect immediately,

        no instance is touched, and the new set is what a later IAM condition

        reads as `basalt:ResourceTag/<key>`. It replaces the whole set — an
        empty

        object clears it, an omitted field leaves it alone.


        A new `template` replaces the stored one wholesale and changes what the

        pool launches NEXT; the instances already running keep what they booted

        with, because a live VM cannot change flavor, tier, subnet or tags in

        place. So a `template.tags` edit leaves the pool holding members with
        two

        different tag sets until it is rolled. The pool reports

        `stale_instance_count` — how many members are on the old template — and

        POST /v1/instance-pools/{pool_id}/refresh rolls them.
      operationId: updateInstancePool
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InstancePoolUpdateRequest'
      responses:
        '200':
          description: The updated instance pool.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InstancePoolResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
        - BearerAuth: []
components:
  schemas:
    InstancePoolUpdateRequest:
      type: object
      additionalProperties: false
      description: >
        Every field is optional; sending none of them is a 400.

        Omitted bounds retain their current values. The resulting bounds must
        satisfy

        0 <= min_count <= max_count <= 100, or the entire request returns 400.

        If desired_count is omitted, it is clamped into the resulting bounds.

        If supplied, desired_count must lie within those bounds or the entire
        request

        returns 400 with no changes applied. A changed target is reconciled
        normally.

        Managed pools are read-only through the customer API.


        A refresh waits when max_count leaves no surge headroom; increasing
        max_count

        allows it to resume.


        The two tag sets move independently. `tags` relabels the pool itself and

        takes effect immediately, touching no instance. `template.tags` — like
        the

        rest of `template` — does NOT touch the instances already running: a
        live

        VM cannot change flavor, tier, subnet or its tags in place. It changes
        what

        the pool launches NEXT, so until you roll the pool it holds members

        carrying two different tag sets, and `stale_instance_count` is how many
        are

        on the older one. Bring them onto the current template with

        POST /v1/instance-pools/{pool_id}/refresh.
      properties:
        description:
          type: string
          description: >-
            Customer note on the pool. Omit to preserve it; send an empty string
            to clear it. Changes no instances, sizing or launch configuration.
          example: Nightly background workers
        tags:
          allOf:
            - $ref: '#/components/schemas/Tags'
          description: >-
            REPLACES the pool's labels: the map you send becomes the whole set,
            an empty object clears them, and omitting the field leaves them
            alone. Replacement rather than a merge because a merge leaves no way
            to say a key should be removed.

            These label the pool, not its instances. To change what future
            replicas are tagged with, send `template.tags`.
        desired_count:
          type: integer
          minimum: 0
          maximum: 100
          description: >-
            New target size, bounded by the resulting min_count/max_count and
            the hard platform cap of 100.
          example: 3
        min_count:
          type: integer
          minimum: 0
          maximum: 100
          description: >-
            New lower bound; omitted desired_count rises to this bound if
            needed.
          example: 2
        max_count:
          type: integer
          minimum: 0
          maximum: 100
          description: >-
            New upper bound; omitted desired_count falls to this bound if
            needed. A value of 0 means the pool holds no members until max_count
            is raised.
          example: 6
        template:
          allOf:
            - $ref: '#/components/schemas/InstancePoolTemplateRequest'
          description: >-
            Replaces the launch config WHOLESALE — the object you send is what
            the pool launches next, and anything you leave out is cleared rather
            than kept. Replacement rather than a deep merge so a shorter
            `networks` or `volumes` cannot be read as a truncation and silently
            drop an interface or a disk.
    InstancePoolResponse:
      type: object
      properties:
        instance_pool:
          $ref: '#/components/schemas/InstancePool'
    Tags:
      type: object
      additionalProperties:
        type: string
      example:
        environment: production
        team: backend
    InstancePoolTemplateRequest:
      type: object
      additionalProperties: false
      required:
        - flavor
        - networks
      description: >-
        The pool's launch config, in the shape a standalone instance create
        takes: same field names, same types, same meanings, so a client that can
        build an instance can build a pool of them without a second, narrower
        contract to learn.

        It belongs to the pool. There is no separate launch-template resource to
        create, version or share between pools.

        Networking is one ordered `networks` list, index 0 being the primary
        NIC. `ip_address` and `mac` are part of that shared NIC shape but are
        refused here: every replica launches from this one template, so a fixed
        address would have the second replica ask for one the first already
        holds.
      properties:
        flavor:
          type: string
          description: Regional flavor reference (UUID, CRN or exact name).
        architecture:
          type: string
          default: amd64
        image:
          type: string
          description: >-
            Image to clone each replica's boot disk from. Accepts the same four
            forms instance create does: an architecture-qualified CRN, an image
            id, `name:version`, or a bare `name`.

            Unlike instance create, the reference is resolved ONCE, when the
            pool is created, and the resulting image id is what every replica
            boots — including replacements spawned months later. A tag
            re-resolved per replica would let a heal boot a newer build than its
            siblings, and a pool whose members are quietly not identical is the
            premise of the primitive breaking silently. To move a pool to a new
            build, change the template.
        networks:
          type: array
          description: >-
            Per-replica interfaces. Index 0 is the primary NIC and is required;
            the rest are extras.
          items:
            $ref: '#/components/schemas/NetworkConfig'
          minItems: 1
        keypairs:
          type: array
          items:
            type: string
            description: >-
              Account-scoped SSH keypair references (UUID, CRN or exact name),
              resolved and pinned for every replica.
        user_data:
          type: string
          format: byte
          description: Base64-encoded user data (cloud-init), stamped on every replica.
        metadata:
          $ref: '#/components/schemas/Metadata'
        tags:
          allOf:
            - $ref: '#/components/schemas/Tags'
          description: >-
            Tags stamped on every instance this template launches. These are the
            replicas' tags, not the pool's — the pool's own labels are the
            top-level `tags`, and the two are independent.

            Changing them affects FUTURE launches only. The instances already
            running keep the tags they were launched with, so between the change
            and a refresh the pool holds members carrying two different tag
            sets; `stale_instance_count` is how many are still on the old one.
            POST /v1/instance-pools/{pool_id}/refresh rolls them onto the
            current template.
        iam_role:
          type: string
          description: >-
            IAM role reference from the same account (UUID, CRN or exact name).
            PassRole and instance trust authorization apply.
        volumes:
          type: array
          items:
            $ref: '#/components/schemas/InstanceVolume'
          description: |
            Per-replica disks, the boot disk included — mark it with
            `boot: true`. Same shape as instance create.
    InstancePool:
      type: object
      required:
        - faults
      description: >-
        A launch template plus a desired count. Creating a pool spawns
        desired_count instances; a reconciler converges member_count toward
        desired_count as it changes. member_count is how many members the pool
        holds; live_count is how many of them are running.

        A pool carries two tag sets and they answer different questions. `tags`
        labels the pool resource — that is what an IAM condition reads as
        `basalt:ResourceTag/<key>` and what a cost report groups by, and it
        reaches no instance. `template.tags` is the set stamped on every replica
        the pool launches.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          example: 8f2a1c3d-4e5b-4a6f-9c0d-1e2f3a4b5c6d
        crn:
          type: string
          readOnly: true
          description: >-
            Cloud Resource Name. This is the value an IAM policy statement must
            name to scope a permission to this pool alone; a policy written
            against anything else will not match.
          example: crn:compute:sa-saopaulo-1:my-account:instance-pool/web-pool
        name:
          description: >-
            Resource names must not start with the literal crn: prefix or be
            UUIDs (canonical, compact, braced, or urn:uuid: forms, in either
            case).
          type: string
          example: web-asg
        description:
          type: string
          example: Front-end autoscaling group
        desired_count:
          type: integer
          minimum: 0
          maximum: 100
          example: 2
        min_count:
          type: integer
          minimum: 0
          maximum: 100
          example: 1
        max_count:
          type: integer
          minimum: 0
          maximum: 100
          description: >-
            A value of 0 means the pool holds no members until max_count is
            raised.
          example: 3
        live_count:
          type: integer
          readOnly: true
          description: |
            How many members are UP — bound instances whose current_state is
            `running`.
          example: 2
        member_count:
          type: integer
          readOnly: true
          description: >-
            How many instances the pool holds, running or not. This is what the
            reconciler converges toward desired_count and what `status`
            reflects, so member_count == desired_count with live_count below it
            means the pool has the members it was asked for and some of them are
            not up.
          example: 2
        refresh_in_progress:
          type: boolean
          readOnly: true
          description: >-
            True while a rolling replacement requested through POST
            /v1/instance-pools/{pool_id}/refresh is still running. It clears
            itself once every member is on the current template. The pool reads
            `scaling` for the duration, since it runs one instance over its
            target while a replacement comes up.
          example: false
        stale_instance_count:
          type: integer
          readOnly: true
          description: >-
            How many members were launched from a template other than the pool's
            current one — that is, how many a refresh would replace. Non-zero
            after editing `template` and before refreshing, which is the signal
            that a template change has not been rolled out yet.
          example: 0
        status:
          type: string
          enum:
            - active
            - scaling
            - error
            - deleting
          readOnly: true
          description: >
            Where the pool is against its target.


            `active` means member_count == desired_count — the pool holds the

            members it was asked for. It is not a claim that all of them are up;

            read live_count for that.


            `scaling` means it does not, and the reconciler is converging it:

            after a create, after a desired_count change, and for the length of
            an

            instance refresh, which runs the pool one instance over its target

            while a replacement comes up.


            `error` means an active error fault exists. Capacity failures remain

            eligible for reconciliation; failed deletion retains its teardown
            intent

            and never recreates members. `deleting` is teardown without an
            active error.
          example: active
        faults:
          type: array
          readOnly: true
          description: >-
            Active faults, newest first. Empty for a healthy pool. Recovery
            resolves only the successful operation's codes.
          items:
            $ref: '#/components/schemas/Fault'
        managed_by:
          type: string
          readOnly: true
          example: customer
        tags:
          allOf:
            - $ref: '#/components/schemas/Tags'
          description: >-
            Labels on the POOL itself, for IAM conditions
            (`basalt:ResourceTag/<key>`) and cost attribution. They are attached
            to nothing else: no instance the pool launches carries them.

            The tags a replica is launched with are `template.tags`. Unlike the
            other top-level fields beside this one, `tags` is not a projection
            of the launch template — it is the pool's own set, and PATCHable on
            its own.
        template:
          allOf:
            - $ref: '#/components/schemas/InstancePoolTemplate'
          readOnly: true
          description: |
            The pool's launch config, in the shape instance create takes. The
            only place it appears: a flat copy of it beside this was two
            spellings of one thing, and two spellings drift.
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
            - request_id
          properties:
            code:
              type: string
              description: Error code identifying the type of error
              example: INVALID_INPUT
            message:
              type: string
              description: Human-readable error message
              example: Invalid request parameters
            request_id:
              type: string
              format: uuid
              description: Request ID for debugging
              example: 550e8400-e29b-41d4-a716-446655440000
    NetworkConfig:
      type: object
      additionalProperties: false
      required:
        - subnet
      properties:
        subnet:
          type: string
          description: >-
            Subnet UUID or complete VPC/subnet CRN. Bare names require a VPC
            parent and are rejected here.
          example: 9b2e4f1a-3c5d-4e6f-8a90-1b2c3d4e5f60
        mac:
          type: string
          description: |
            Optional MAC address. Must be locally-administered (`X2:`,
            `X6:`, `XA:`, `XE:` in the first octet). Generated when
            omitted.
          example: 02:1a:2b:3c:4d:5e
        security_groups:
          type: array
          items:
            type: string
            example: c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f
          description: >
            Account-scoped security group references (UUID, CRN or name) to
            attach to this NIC.

            Each must be owned by the same account. Empty list = no

            per-NIC ACLs (the platform's default-allow stays in

            force).
        floating_ip_assignment:
          type: string
          enum:
            - none
            - ipv4
            - ipv6
            - dual_stack
            - auto
          default: none
          description: >-
            Allocate public floating IPs for this NIC at launch. Explicit
            families require matching guest addresses and internet routes.
            Detach leaves the FIP reserved. No ordinary public IPv4 mapping
            exists.
        addresses:
          type: array
          items:
            $ref: '#/components/schemas/AddressRequest'
    Metadata:
      type: object
      additionalProperties:
        type: string
      example:
        environment: production
        team: backend
    InstanceVolume:
      type: object
      required:
        - size_gb
      description: |
        One disk created with the instance. `boot: true` marks the one cloned
        from image_id; every other entry is a blank volume the in-guest agent
        formats and mounts.
      properties:
        boot:
          type: boolean
          default: false
          example: false
          description: |
            Marks the boot disk. It takes no mount_path or fstype —
            both come from the image — and sending either is refused
            rather than ignored.
        size_gb:
          type: integer
          minimum: 1
          example: 20
        volume_type:
          type: string
          example: nvme
          description: Tier; omitted = the region default.
        mount_path:
          type: string
          example: /data
        fstype:
          type: string
          example: ext4
          description: Filesystem the in-guest agent formats the volume with.
        delete_on_termination:
          type: boolean
          default: true
          example: true
          description: Destroyed with the instance unless set false.
    Fault:
      type: object
      required:
        - code
        - severity
        - message
        - details
        - first_at
        - last_at
        - occurrences
      properties:
        code:
          type: string
          description: Stable machine-readable code owned by the reporting operation.
          example: BACKUP_FAILED
        severity:
          type: string
          enum:
            - error
            - warning
        message:
          type: string
          example: Backup upload failed.
        details:
          type: object
          nullable: true
          additionalProperties: true
          description: Structured context; legacy strings are preserved in legacy_text.
        first_at:
          type: string
          format: date-time
          description: First observation in this active occurrence series.
        last_at:
          type: string
          format: date-time
          description: Latest observation in this active occurrence series.
        occurrences:
          type: integer
          minimum: 1
          example: 1
    InstancePoolTemplate:
      type: object
      description: >-
        Stored launch configuration with canonical UUID relationship identities.
        Convert these identities to the request fields in
        InstancePoolTemplateRequest when replacing the template. The image and
        keypair identities are pinned; later name reuse or a new current image
        version does not change them.
      properties:
        flavor_id:
          type: string
          format: uuid
        image_id:
          type: string
          format: uuid
          description: >-
            Resolved image UUID pinned for every replica until template
            replacement.
        networks:
          type: array
          description: >-
            Per-replica interfaces. Index 0 is the primary NIC and is required;
            the rest are extras.
          items:
            $ref: '#/components/schemas/NetworkConfigResponse'
          minItems: 1
        key_names:
          type: array
          items:
            type: string
            format: uuid
            description: Canonical SSH keypair UUID pinned for every replica.
        user_data:
          type: string
          format: byte
          description: Base64-encoded user data (cloud-init), stamped on every replica.
        metadata:
          $ref: '#/components/schemas/Metadata'
        tags:
          allOf:
            - $ref: '#/components/schemas/Tags'
          description: >-
            Tags stamped on every instance this template launches. These are the
            replicas' tags, not the pool's — the pool's own labels are the
            top-level `tags`, and the two are independent.

            Changing them affects FUTURE launches only. The instances already
            running keep the tags they were launched with, so between the change
            and a refresh the pool holds members carrying two different tag
            sets; `stale_instance_count` is how many are still on the old one.
            POST /v1/instance-pools/{pool_id}/refresh rolls them onto the
            current template.
        iam_role:
          allOf:
            - $ref: '#/components/schemas/InstanceRole'
          description: >-
            Summary of the IAM role attached to every replica, visible with pool
            read access without iam:GetRole. Omitted when no role is attached,
            the role was deleted, or it belongs to another account. Sensitive
            role fields remain available only through the IAM API.
        volumes:
          type: array
          items:
            $ref: '#/components/schemas/InstanceVolume'
          description: |
            Per-replica disks, the boot disk included — mark it with
            `boot: true`. Same shape as instance create.
    AddressRequest:
      type: object
      properties:
        family:
          type: string
          enum:
            - ipv4
            - ipv6
        address:
          type: string
          description: >-
            Optional fixed IPv4 address. Omit for IPv6; IPAM allocates an
            aligned /96.
      required:
        - family
      additionalProperties: false
    NetworkConfigResponse:
      type: object
      required:
        - subnet
      properties:
        subnet:
          anyOf:
            - $ref: '#/components/schemas/Subnet'
            - type: object
              nullable: true
              enum:
                - null
          description: Subnet placement; null when the referenced subnet no longer exists.
        mac:
          type: string
          description: |
            Optional MAC address. Must be locally-administered (`X2:`,
            `X6:`, `XA:`, `XE:` in the first octet). Generated when
            omitted.
          example: 02:1a:2b:3c:4d:5e
        security_group_ids:
          type: array
          items:
            type: string
            format: uuid
            example: c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f
          description: >
            Account-scoped security group references (UUID, CRN or name) to
            attach to this NIC.

            Each must be owned by the same account. Empty list = no

            per-NIC ACLs (the platform's default-allow stays in

            force).
        floating_ip_assignment:
          type: string
          enum:
            - none
            - ipv4
            - ipv6
            - dual_stack
            - auto
          default: none
          description: >-
            Allocate public floating IPs for this NIC at launch. Explicit
            families require matching guest addresses and internet routes.
            Detach leaves the FIP reserved. No ordinary public IPv4 mapping
            exists.
        addresses:
          type: array
          items:
            $ref: '#/components/schemas/AddressRequest'
    InstanceRole:
      type: object
      required:
        - id
        - crn
        - name
      properties:
        id:
          type: string
          format: uuid
          example: b2c3d4e5-f6a7-8901-2345-67890abcdef1
        crn:
          type: string
          description: Account-scoped role identity, as used in policy documents.
          example: crn:iam::my-account:role/deploy
        name:
          type: string
          description: Immutable role name.
          example: deploy
    Subnet:
      type: object
      required:
        - id
        - crn
        - vpc
        - route_table
        - name
        - cidr_ipv4
        - gateway_ipv4
        - tags
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        crn:
          type: string
          readOnly: true
          example: crn:network:sa-saopaulo-1:my-account:vpc/prod/subnet/prod-web
        vpc:
          $ref: '#/components/schemas/Vpc'
        route_table:
          $ref: '#/components/schemas/RouteTableSummary'
        name:
          description: >-
            Resource names must not start with the literal crn: prefix or be
            UUIDs (canonical, compact, braced, or urn:uuid: forms, in either
            case).
          type: string
          example: prod-web
        description:
          type: string
          example: Public web-tier subnet
        cidr_ipv4:
          type: string
          example: 10.0.1.0/24
        gateway_ipv4:
          type: string
          example: 10.0.1.1
        cidr_ipv6:
          type: string
          nullable: true
          readOnly: true
          description: >-
            The dual-stack IPv6 /64, if the subnet is v6-enabled. Its presence
            (vs the v4 cidr_ipv4) is how a client tells the subnet's families
            apart.
          example: 2a13:9500:1a6:100::/64
        gateway_ipv6:
          type: string
          nullable: true
          readOnly: true
          example: 2a13:9500:1a6:100::1
        tags:
          type: object
          additionalProperties:
            type: string
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          readOnly: true
    Vpc:
      type: object
      required:
        - id
        - crn
        - name
        - cidr_ipv4
        - tags
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        crn:
          type: string
          readOnly: true
          description: Cloud Resource Name (name-based, region+account-scoped).
          example: crn:network:sa-saopaulo-1:my-account:vpc/prod
        name:
          type: string
          description: >-
            1-63 chars, lowercase alphanumeric + hyphen Resource names must not
            start with the literal crn: prefix or be UUIDs (canonical, compact,
            braced, or urn:uuid: forms, in either case).
          example: prod
        description:
          type: string
          example: Production VPC for web and app tiers
        cidr_ipv4:
          type: string
          description: >-
            IPv4 CIDR block carved up by subnets. Must be private (RFC 1918):
            within 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16. Immutable after
            create.
          example: 10.0.0.0/16
        cidr_ipv6:
          type: string
          nullable: true
          readOnly: true
          description: Associated regional GUA or private ULA prefix.
          example: 2a13:9500:1a6:100::/60
        tags:
          type: object
          additionalProperties:
            type: string
        created_at:
          type: string
          format: date-time
          readOnly: true
        updated_at:
          type: string
          format: date-time
          readOnly: true
    RouteTableSummary:
      type: object
      nullable: true
      additionalProperties: false
      description: |
        Route table used by a subnet, without repeating its VPC. Null when the
        non-owning lookup no longer resolves, for example during concurrent
        reassociation and deletion of the former table. Deleting a table still
        associated with subnets is refused.
      required:
        - id
        - crn
        - name
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        crn:
          type: string
          readOnly: true
          example: >-
            crn:network:sa-saopaulo-1:my-account:vpc/prod/route-table/prod-private-rt
        name:
          type: string
          example: prod-private-rt
  responses:
    BadRequest:
      description: Invalid request parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INVALID_INPUT
              message: Invalid request parameters
              request_id: 550e8400-e29b-41d4-a716-446655440000
    Unauthorized:
      description: Authentication required or token invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: UNAUTHORIZED
              message: Authentication required
              request_id: 550e8400-e29b-41d4-a716-446655440000
    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: ACCESS_DENIED
              message: You don't have permission to perform this action
              request_id: 550e8400-e29b-41d4-a716-446655440000
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: NOT_FOUND
              message: Resource not found
              request_id: 550e8400-e29b-41d4-a716-446655440000
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        An OAuth 2.0 bearer token, sent as `Authorization: Bearer <token>`.
        This is the recommended way to authenticate.

        Get one by exchanging a service account's access key pair at
        `POST /v1/oauth/token` with `grant_type=client_credentials`. It is the
        standard client-credentials grant, so any OAuth-aware library will
        obtain and refresh it for you.

        ```
        curl -s -u "$KEY_ID:$SECRET" -d grant_type=client_credentials \
          https://iam.basaltic.sh/v1/oauth/token
        ```

        Tokens last an hour by default. The same access key pair is separately
        your AWS SigV4 credential for the S3-compatible object endpoint, which
        speaks nothing else.

````