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

> List DNS zones owned by the current organization, newest first.
Keyset-paginated by zone id (UUIDv7 sorts by creation time) —
pass the last id from the previous page as `marker` to fetch
the next.




## OpenAPI

````yaml /api-reference/specs/dns.yaml get /v1/zones
openapi: 3.0.3
info:
  title: Basaltic DNS API
  version: 1.0.0
  description: |
    Authoritative hosted zones and their records, signed with DNSSEC. A zone
    is only served once its ownership is verified; associating a zone with a
    VPC makes it resolvable privately inside that network.
  contact:
    name: Basaltic Support
    email: ping@basaltic.sh
  license:
    name: Proprietary
    url: https://basaltic.sh/terms
servers:
  - url: https://dns.basaltic.sh
    description: Global API endpoint
security:
  - SignatureAuth: []
paths:
  /v1/zones:
    get:
      tags:
        - DNS
      summary: List zones
      description: |
        List DNS zones owned by the current organization, newest first.
        Keyset-paginated by zone id (UUIDv7 sorts by creation time) —
        pass the last id from the previous page as `marker` to fetch
        the next.
      operationId: listZones
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 50
            example: 50
        - name: marker
          in: query
          schema:
            type: string
            format: uuid
            example: 550e8400-e29b-41d4-a716-446655440000
          description: Resume token — the last zone id from the previous page.
      responses:
        '200':
          description: List of zones
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ZoneListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
        - SignatureAuth: []
components:
  schemas:
    ZoneListResponse:
      type: object
      properties:
        zones:
          type: array
          items:
            $ref: '#/components/schemas/Zone'
        meta:
          $ref: '#/components/schemas/PaginationMeta'
    Zone:
      type: object
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          example: 550e8400-e29b-41d4-a716-446655440000
        crn:
          type: string
          readOnly: true
          description: Cloud Resource Name.
          example: crn:dns::my-account:zone/example.com
        name:
          type: string
          description: Zone FQDN.
          example: example.com
        nameservers:
          type: array
          items:
            type: string
          description: |
            Authoritative nameservers for the zone — the apex NS set.
            Copy this list verbatim into your registrar's nameserver
            configuration to delegate the zone to the platform.
          example:
            - ns1.cloud.bycoded.dev
            - ns2.cloud.bycoded.dev
        soa:
          $ref: '#/components/schemas/SOA'
        visibility:
          type: string
          enum:
            - public
            - private
          readOnly: true
          description: |
            `public` zones answer on the internet-facing nameservers; `private`
            zones answer only inside the VPCs associated with them (see the
            vpc-associations endpoints). Fixed at creation.
          example: public
        dnssec:
          $ref: '#/components/schemas/ZoneDNSSEC'
        tags:
          $ref: '#/components/schemas/Tags'
        ownership:
          $ref: '#/components/schemas/ZoneOwnership'
    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
            request_id:
              type: string
              format: uuid
              description: Request ID for debugging
              example: 550e8400-e29b-41d4-a716-446655440000
    SOA:
      type: object
      properties:
        primary_ns:
          type: string
          description: SOA `mname` — first authoritative nameserver for the zone.
          example: ns1.cloud.bycoded.dev
        admin_email:
          type: string
          format: email
          readOnly: true
          description: |
            SOA `rname` — the platform's DNS-operations contact.
            Stamped server-side; not customer-configurable.
          example: dns@bycoded.dev
        refresh:
          type: integer
          example: 10800
        retry:
          type: integer
          example: 3600
        expire:
          type: integer
          example: 604800
        minimum:
          type: integer
          description: NXDOMAIN cache TTL.
          example: 3600
    ZoneDNSSEC:
      type: object
      description: |
        DNSSEC signing state. Present once the signer has bootstrapped the zone,
        which is always-on for zones created through this API; absent on a zone
        that is not signed.

        `ds_records` is what the customer pastes at the parent registrar to
        complete the chain of trust. `rdata` is the zone-file form of the same
        record, for registrars that take one string.
      required:
        - enabled
      properties:
        enabled:
          type: boolean
          readOnly: true
          example: true
        ksk_key_tag:
          type: integer
          readOnly: true
          description: Key tag of the key-signing key — matches the DS records below.
          example: 12345
        zsk_key_tag:
          type: integer
          readOnly: true
          description: Key tag of the zone-signing key.
          example: 54321
        algorithm:
          type: integer
          readOnly: true
          description: DNSSEC algorithm number. 13 = ECDSA P-256 SHA-256.
          example: 13
        ds_records:
          type: array
          readOnly: true
          items:
            $ref: '#/components/schemas/ZoneDSRecord'
    Tags:
      type: object
      additionalProperties:
        type: string
      example:
        environment: production
        team: backend
    ZoneOwnership:
      type: object
      description: >
        Domain-ownership proof. A zone with no existing zone above it on the

        platform does not resolve until `verified` is true — publish the TXT

        record below at your current DNS provider, then POST

        /v1/zones/{zone_id}/verify-ownership.


        If the domain is already delegated to us there is nowhere to publish
        that

        TXT, because we are its current DNS and an unverified zone does not
        answer.

        Use `claim_nameservers` instead.


        A zone created underneath a zone you already own inherits its parent's

        proof and is verified on creation.


        The proof is a lease, not a deed: it is re-confirmed periodically for as

        long as the zone is served, so a domain that lapses or changes hands
        stops

        being served from here. `checked_at` is the last confirmation;

        `recheck_deadline` appears only while re-confirmation is failing and is

        the field to render a warning from.
      properties:
        verified:
          type: boolean
          readOnly: true
          description: >-
            Whether the zone has proved ownership. Unverified zones do not
            resolve.
          example: false
        verified_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
          description: When ownership was first proved. Absent while unverified.
          example: '2026-01-15T09:30:00Z'
        checked_at:
          type: string
          format: date-time
          readOnly: true
          description: >
            When the proof was last re-confirmed. `verified_at` keeps meaning

            "first demonstrated" and does not move; this does, on every pass
            that

            passes. Absent until the zone has been re-confirmed at least once.
          example: '2026-04-14T09:30:00Z'
        recheck_deadline:
          type: string
          format: date-time
          readOnly: true
          description: >
            Present ONLY while the periodic re-proof is failing: the instant the

            zone stops answering unless it passes again. Absent means healthy.


            Each pass re-checks a failing zone, so reaching this date takes

            sustained failure, not one bad afternoon — a transient resolver

            problem clears itself on the next pass. To clear it deliberately,

            republish the challenge TXT (or point the domain's delegation back
            at

            our nameservers) and POST /v1/zones/{zone_id}/verify-ownership.


            The organization's owner is emailed when this date is set, and again

            if the zone does stop resolving. A zone is never taken off the air

            before that message has gone out, so the date here is the earliest

            the zone can stop answering and never the only warning.
          example: '2026-04-28T09:30:00Z'
        record_type:
          type: string
          readOnly: true
          example: TXT
        record_name:
          type: string
          readOnly: true
          description: Owner name the challenge TXT must be published at.
          example: _basaltic-challenge.example.com
        record_value:
          type: string
          readOnly: true
          description: Value the challenge TXT must contain.
          example: 9f2c1e7a4b8d6035e1a4c9f27b0d8e63a5f1c4b90d2e7a836f5c1b904e7d2a68
        claim_nameservers:
          type: array
          readOnly: true
          description: >
            The other way to prove ownership: set these as the domain's

            nameservers at your registrar, then POST

            /v1/zones/{zone_id}/verify-ownership. This is the route for a domain

            already delegated to us, where the challenge TXT can never resolve.


            These names reach the same servers the zone's `nameservers` do, but

            they are unique to this zone, and that is what makes them proof.

            Anyone can point a domain at our published nameservers, so a
            delegation

            naming those shows only that somebody did so at some point —
            possibly

            a previous owner who never updated their registrar. A delegation
            naming

            these can only have been set for this zone, by whoever holds the
            domain

            now.


            The delegation may stay on these names permanently.
          items:
            type: string
          example:
            - ns1.9f2c1e7a4b8d6035.claim.dns.basaltic.cloud
            - ns2.9f2c1e7a4b8d6035.claim.dns.basaltic.cloud
    ZoneDSRecord:
      type: object
      description: One DS record to publish at the parent zone.
      required:
        - key_tag
        - algorithm
        - digest_type
        - digest
        - rdata
      properties:
        key_tag:
          type: integer
          example: 12345
        algorithm:
          type: integer
          example: 13
        digest_type:
          type: integer
          description: 2 = SHA-256.
          example: 2
        digest:
          type: string
          example: B8FA03AA1BB49CC702C32C5C3FE6061BBDD0808371F43A28A8E8BE2738CC9861
        rdata:
          type: string
          description: >-
            Full zone-file form — `<key_tag> <algorithm> <digest_type>
            <digest>`.
          example: >-
            12345 13 2
            B8FA03AA1BB49CC702C32C5C3FE6061BBDD0808371F43A28A8E8BE2738CC9861
  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
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INTERNAL_ERROR
              message: An internal error occurred
              request_id: 550e8400-e29b-41d4-a716-446655440000
  securitySchemes:
    SignatureAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >
        Request signing with an access key issued to a service account. An

        HMAC-SHA256 over a canonical form of the request, close to AWS SigV4.

        The `basaltic` CLI signs for you.


        Send `Authorization`, `X-Date` (UTC, `YYYYMMDDTHHMMSSZ`) and `X-Nonce`

        (random per request); add `X-Content-Sha256` to bind a body, and

        `X-Amz-Security-Token` when using temporary credentials.


        ```

        Authorization: BASALTIC-HMAC-SHA256
        Credential=<access_key_id>/<date>/<region>/basaltic/basaltic_request,
        SignedHeaders=host;x-date;x-nonce, Signature=<hex>

        ```


        `<region>` is the region code you are calling, or `global` for the
        global

        services. A signature is valid for 5 minutes from `X-Date`, and mutating

        requests are replay-guarded on the nonce.


        **Full signing procedure, including a working implementation:**

        https://docs.basaltic.sh/authentication


        ## Rate limits

        There is no global request budget. A limit applies only where an

        operation documents a `429`, and that operation says what it counts.

        Those responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`,

        `X-RateLimit-Reset` and, on a `429`, `Retry-After` — read them rather

        than hard-coding a number. Retrying before `Retry-After` is refused and

        extends the window. Everything else is bounded by quota, not by request

        rate.

````