> ## 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.

# List images

> Every manifest in the repository with the tags pointing at it.

Filter with `tag_status=untagged` to find the residue of moved
tags — the images that accumulate when `:latest` is pushed
repeatedly, and usually the answer to "why is this repository so
large".


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


## OpenAPI

````yaml /api-reference/specs/registry.yaml get /v1/repositories/{repository_id}/images
openapi: 3.0.3
info:
  title: Basaltic Registry API
  version: 1.0.0
  description: >
    KMS bindings use `kms_key`: a UUID, account-scoped name, or complete

    `crn:kms:<region>:<account>:key/<name>` in the authenticated account and
    current region. The key must be enabled,

    symmetric, and usable for encryption. Bindings retain the key's immutable

    identity; responses render a ResourceRef as `kms_key`. Invalid or
    foreign-scope CRNs are

    rejected with HTTP 400; missing keys return HTTP 404 on binding or

    cryptographic use. Metadata reads remain available for deleted bindings,

    with a nameless `kms_key` reference retaining the immutable key identity.

    Private container image registries.


    **This spec documents the control plane, not the pull path.** The

    management API uses `registry.<region>.basaltic.sh`; Docker and OCI

    clients use `cr.<region>.basaltic.cloud`. Everything below is the

    `/v1` REST surface — repositories, images, lifecycle policies,

    resource policies — authenticated with a platform OAuth 2.0 bearer token

    or a console session, like every other Basaltic API. Separately, `/v2`

    implements the [OCI Distribution

    Specification](https://github.com/opencontainers/distribution-spec)

    v1.1, which is what `docker`, `podman`, `buildah`, `crane`, `skopeo`,

    and every Kubernetes node's container runtime speak. That surface is

    not described here because it is not ours to describe: its paths,

    status codes, headers, and error codes are fixed by the spec, and a

    client that finds anything else fails.


    **Getting a docker credential.** `POST /v1/authorization-token`

    returns a short-lived password. Feed it to `docker login`; the daemon

    then exchanges it at `/v2/token` for per-repository bearer tokens on

    its own, which is where the actual authorization decision is made.

    The login credential carries no authority of its own — revoking a

    policy takes effect within one bearer token's lifetime (five minutes)

    rather than at the end of a twelve-hour login.


    **The account handle is part of the image reference.** A region

    serves one registry host, so an image lives at

    `cr.<region>.basaltic.cloud/<account_handle>/<repository>:<tag>`. That

    first path component is the only thing separating two tenants'

    `api` repositories, which is why every repository response includes a

    ready-made `uri` rather than leaving clients to assemble one.


    **Everything except a tag is immutable.** A blob is named by the

    sha256 of its bytes and a manifest by the sha256 of its own JSON, so

    pushing the same content twice is a no-op the client detects before it

    uploads a byte. A tag is the one mutable pointer — and a repository

    may forbid even that, with `tag_mutability: immutable`, so a deployed

    digest cannot be swapped under a running fleet.


    **Deleting by digest and deleting by tag are different.** Removing a

    digest removes the manifest and every tag pointing at it. Removing a

    tag leaves the manifest in place, untagged: another tag may still name

    it, and `docker rmi` of one tag must not destroy an image someone else

    is pulling by another. Untagged manifests are what the `untagged`

    lifecycle rule exists to sweep.


    **Layers are shared and counted once.** Blobs are deduplicated across

    every repository in an account, so a repository's `size_bytes` is the

    sum of the distinct blobs its manifests reference — not the sum of its

    images' sizes, which would count every shared base layer once per

    image. Deleting an image does not immediately free space: whether a

    layer is still needed is a question about every other repository in

    the account, and the answer comes from a background sweep.


    **Sharing a repository.** A repository is private to its account

    until you attach a resource policy. `PUT

    /v1/repositories/{id}/policy` is how another account — or, with

    principal `*`, the public — is granted pull access. Cross-account

    access requires BOTH the resource policy and the caller's own IAM

    policy to allow it, so one tenant cannot grant another tenant's

    employee more than that employee's organization permits.
  contact:
    name: Basaltic Support
    email: ping@basaltic.sh
  license:
    name: Proprietary
    url: https://basaltic.sh/terms
servers:
  - url: https://registry.{region}.basaltic.sh
    description: Regional API endpoint
    variables:
      region:
        default: sa-saopaulo-1
        description: Region code
security:
  - BearerAuth: []
tags:
  - name: Authorization
    description: The credential `docker login` consumes
  - name: Repositories
    description: Repository lifecycle and settings, and the images and tags in one
  - name: Policies
    description: Resource policies and lifecycle policies
  - name: Encryption
    description: At-rest encryption of layer bytes
  - name: Pull-through cache
    description: Mirroring an upstream registry's images into a namespace of your own
paths:
  /v1/repositories/{repository_id}/images:
    parameters:
      - $ref: '#/components/parameters/RepositoryId'
    get:
      tags:
        - Repositories
      summary: List images
      description: |
        Every manifest in the repository with the tags pointing at it.

        Filter with `tag_status=untagged` to find the residue of moved
        tags — the images that accumulate when `:latest` is pushed
        repeatedly, and usually the answer to "why is this repository so
        large".
      operationId: describeImages
      parameters:
        - $ref: '#/components/parameters/TagStatus'
        - $ref: '#/components/parameters/TagPrefix'
        - $ref: '#/components/parameters/Marker'
        - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Paginated list of images.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImageListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  parameters:
    RepositoryId:
      name: repository_id
      in: path
      required: true
      schema:
        type: string
        format: uuid
        example: a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d
    TagStatus:
      name: tag_status
      in: query
      description: |
        Restrict to one population. `untagged` finds the residue of moved
        tags, which is usually what fills a repository up.
      schema:
        type: string
        enum:
          - tagged
          - untagged
          - any
        example: untagged
    TagPrefix:
      name: tag_prefix
      in: query
      description: Return only images carrying a tag with this prefix.
      schema:
        type: string
        example: prod-
    Marker:
      name: marker
      in: query
      description: Id of the last item on the previous page.
      schema:
        type: string
        format: uuid
        example: a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d
    Limit:
      name: limit
      in: query
      description: Maximum items to return.
      schema:
        type: integer
        default: 50
        minimum: 1
        maximum: 200
        example: 50
  schemas:
    ImageListResponse:
      type: object
      properties:
        images:
          type: array
          items:
            $ref: '#/components/schemas/Image'
        meta:
          $ref: '#/components/schemas/PaginationMeta'
      required:
        - images
    Image:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: The image's row id — the pagination cursor for the list endpoint.
        digest:
          type: string
          description: The sha256 of the manifest document's exact bytes.
          example: >-
            sha256:1f3c9e0b8d7a6f5e4c3b2a190807f6e5d4c3b2a1908f7e6d5c4b3a2019f8e7d6c
        media_type:
          type: string
          description: |
            The OCI or Docker v2 media type. Both vocabularies are stored
            as pushed: containerd emits OCI, older `docker push` emits
            Docker v2, and they are the same documents with different
            labels.
          example: application/vnd.oci.image.manifest.v1+json
        tags:
          type: array
          items:
            type: string
          description: Every tag pointing at this manifest. Empty for an untagged image.
          example:
            - v1.4.2
            - latest
        artifact_type:
          type: string
          description: |
            What kind of thing this is. Distinguishes a container image
            from a Helm chart, an SBOM, or a signature — all of which are
            stored as manifests and would otherwise be indistinguishable.
          example: application/vnd.oci.image.config.v1+json
        config_digest:
          type: string
          description: Absent on a multi-platform index, which has no config of its own.
        subject:
          type: string
          description: |
            The digest this manifest is *about*, set on a signature, SBOM,
            or attestation. What the OCI referrers API resolves.
        manifest_size_bytes:
          type: integer
          format: int64
          description: The manifest document itself.
        size_bytes:
          type: integer
          format: int64
          description: |
            The sum of the distinct blobs this manifest references — what a
            customer means by "how big is this image".
          example: 87654321
        is_index:
          type: boolean
          description: True for a multi-platform image index or Docker manifest list.
        pushed_at:
          type: string
          format: date-time
        last_pulled_at:
          type: string
          format: date-time
          nullable: true
          description: |
            Coarsened to a minute. A fleet of 500 nodes pulling one image
            in a few seconds would otherwise be 500 row updates contending
            on one page, and the lifecycle rule reading this measures in
            days.
      required:
        - id
        - digest
        - media_type
        - tags
        - size_bytes
        - is_index
        - pushed_at
    PaginationMeta:
      type: object
      properties:
        total:
          type: integer
          description: Total number of items
          example: 150
        limit:
          type: integer
          description: Number of items per page
          example: 20
        marker:
          type: string
          description: >-
            Opaque cursor for the next page. Pass it back as the `marker` query
            parameter; treat it as a token, not a value to parse.
          example: 550e8400-e29b-41d4-a716-446655440000
        has_more:
          type: boolean
          description: Whether there are more items
          example: true
    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
            params:
              type: object
              additionalProperties: true
              description: >-
                Optional non-sensitive values for interpolation of localized
                errors, keyed by error code. Never present for server errors.
              example:
                instances: 2
                pools: 0
            request_id:
              type: string
              format: uuid
              description: Request ID for debugging
              example: 550e8400-e29b-41d4-a716-446655440000
  responses:
    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.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.