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

# List sites

> Every site of the organisation, in byte order of domain. Complete: no pagination.



## OpenAPI

````yaml /openapi-management.json get /v1/sites
openapi: 3.1.0
info:
  title: doubleshift management API
  version: v1
  description: >-
    The management API writes the objects a publisher organisation is made of —
    sites, fragments, the site default, routing rules, card eligibility and
    delivery API keys — with the same rules and the same error messages as the
    console. It is a separate contract from the fragment endpoint
    (`openapi.json`): another credential, and no card serving. `info.version`
    names the contract (`/v1`), not a console release. Guide:
    https://docs.shftd2.com/management-api


    **Host.** `https://api.shftd2.com` only, paths under `/v1`. The console host
    `app.shftd2.com` answers `404` on them.


    **Authentication.** `Authorization: Bearer ds_mgmt_…`: a management token,
    created by an owner in the console (**Settings → Management tokens**) and
    shown once. A token acts on its whole organisation. Cookies are never read.
    A delivery API key (`ds_live_…`) is refused here, and a management token is
    refused by the fragment endpoint.


    **Requests.** Every PUT, POST and DELETE sends `Content-Type:
    application/json` (parameters such as `charset` allowed) and a JSON object —
    `{}` for a DELETE. The body is capped at 262,144 bytes, counted on the raw
    bytes (a UTF-8 byte order mark included) before any parsing: over the cap,
    `400 body_too_large`. Unknown fields are refused (`400 unknown_field`),
    nested ones included. A GET takes no body.


    **Responses.** JSON, with `Cache-Control: private, no-store` and
    `RateLimit-Policy` on every response, success or error. Lists are `{"items":
    [...]}` and complete: there is no pagination. Errors are `{"error": {"code",
    "message", "field"?, "retry_after"?}}`. A resource that does not exist and
    one that belongs to another organisation get the same `404`.


    **Order of checks.** Token and limits (`401`, `429`); then the body: media
    type, size, JSON, unknown fields (`400`), then its shape (`422`); then the
    addressed site, card or key (`404`); then what depends on stored data
    (`403`, `409`, `422`). Nothing is written before every check has passed.


    **Limits.** Per token: 120 requests per minute, of which at most 60 writes
    (PUT, POST, DELETE). Per client IP address: 30 failed authentications per
    minute (no token, or a malformed, unknown, revoked or expired one), and 600
    requests per minute presenting a well-formed token. Over a limit: `429
    rate_limited` with `Retry-After: 60`, and nothing is written. The counters
    are kept per Cloudflare location, so the limits are approximate, and no
    remaining count is sent.
servers:
  - url: https://api.shftd2.com
    description: >-
      The machine host. The management API exists on this host only; there is no
      alias.
security:
  - bearerAuth: []
tags:
  - name: Account
    description: The organisation a token acts on.
  - name: Sites
    description: Sites, addressed by domain.
  - name: Fragments
    description: Fragments, addressed by site domain and reference.
  - name: Site default
    description: The fragment a page resolves to when no routing rule matches.
  - name: Routing rules
    description: Paths that resolve to a fragment of the site.
  - name: Cards
    description: Cards, read-only through this API.
  - name: Eligibility
    description: >-
      The fragments a card is eligible on. Several cards can be eligible on the
      same fragment.
  - name: API keys
    description: Delivery API keys for the fragment endpoint.
paths:
  /v1/sites:
    get:
      tags:
        - Sites
      summary: List sites
      description: >-
        Every site of the organisation, in byte order of domain. Complete: no
        pagination.
      operationId: listSites
      responses:
        '200':
          description: The sites.
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControl'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimitPolicy'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SiteList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/Internal'
      x-codeSamples:
        - lang: bash
          label: curl
          source: |-
            curl -sS "https://api.shftd2.com/v1/sites" \
              -H "Authorization: Bearer $DS_MANAGEMENT_TOKEN"
components:
  headers:
    CacheControl:
      description: On every response of this API.
      schema:
        type: string
        const: private, no-store
    RateLimitPolicy:
      description: >-
        On every response of this API: 120 requests and 60 writes per 60 seconds
        per token (IETF draft ratelimit-headers syntax). No remaining count is
        sent.
      schema:
        type: string
        const: '"token";q=120;w=60, "write";q=60;w=60'
    WWWAuthenticate:
      description: On every 401.
      schema:
        type: string
        const: Bearer realm="doubleshift"
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        const: 60
  schemas:
    SiteList:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/Site'
      example:
        items:
          - id: sit_examplesite1
            account_id: acc_example1
            domain: news-site.example
            name: News site
            favicon_url: null
            default_fragment_id: frg_examplefr1
            default_fragment_ref: /1234567/news-site/organic/politics
            created_at: '2026-09-26T09:00:00.000Z'
    Site:
      type: object
      description: 'A site: a domain the organisation publishes on.'
      required:
        - id
        - account_id
        - domain
        - name
        - favicon_url
        - default_fragment_id
        - default_fragment_ref
        - created_at
      properties:
        id:
          type: string
          description: Stable site id (`sit_…`).
        account_id:
          type: string
          description: The organisation id (`acc_…`).
        domain:
          type: string
          description: >-
            The bare domain, normalized: lowercase, without scheme, path or
            port. `www.` and the apex are two sites.
        name:
          type:
            - string
            - 'null'
          description: Display name, or null.
        favicon_url:
          type:
            - string
            - 'null'
          description: >-
            The site icon. Null for a site created through this API until the
            site is first opened in the console.
        default_fragment_id:
          type:
            - string
            - 'null'
          description: >-
            The site default fragment id (`frg_…`), or null when the site has no
            default.
        default_fragment_ref:
          type:
            - string
            - 'null'
          description: >-
            The site default fragment reference; null when the site has no
            default, or when that fragment has no reference.
        created_at:
          type: string
          format: date-time
          description: Creation time, UTC.
      example:
        id: sit_examplesite1
        account_id: acc_example1
        domain: news-site.example
        name: News site
        favicon_url: null
        default_fragment_id: frg_examplefr1
        default_fragment_ref: /1234567/news-site/organic/politics
        created_at: '2026-09-26T09:00:00.000Z'
    Error:
      type: object
      description: >-
        Every error of this API. Like every response, it carries `Cache-Control:
        private, no-store` and `RateLimit-Policy: "token";q=120;w=60,
        "write";q=60;w=60`.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - unsupported_media_type
                - body_too_large
                - invalid_json
                - unknown_field
                - missing_token
                - invalid_token
                - platform_card
                - not_found
                - conflict
                - validation
                - rate_limited
                - internal
              description: Stable, machine-readable code. Branch on it, not on `message`.
            message:
              type: string
              description: >-
                Human-readable explanation, the console wording where the
                console has one. May change.
            field:
              type: string
              description: >-
                The field at fault, nested ones included
                (`routes[3].path_prefix`, `fragments[0].ref`). Present on
                `unknown_field`, `validation` and `conflict`; absent otherwise.
            retry_after:
              type: integer
              const: 60
              description: Seconds to wait. Present on `rate_limited` only.
      example:
        error:
          code: validation
          message: Choose a fragment of this site
          field: routes[0].ref
  responses:
    Unauthorized:
      description: >-
        `missing_token`: no `Authorization: Bearer` header. `invalid_token`:
        anything else that is not a live management token of an organisation —
        malformed, unknown, revoked or expired, or a delivery API key
        (`ds_live_…`). Cookies are never read. Carries `WWW-Authenticate: Bearer
        realm="doubleshift"`.
      headers:
        Cache-Control:
          $ref: '#/components/headers/CacheControl'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimitPolicy'
        WWW-Authenticate:
          $ref: '#/components/headers/WWWAuthenticate'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing_token:
              value:
                error:
                  code: missing_token
                  message: >-
                    missing management token — send "Authorization: Bearer
                    ds_mgmt_…"
            invalid_token:
              value:
                error:
                  code: invalid_token
                  message: invalid management token
    RateLimited:
      description: >-
        A limit was reached (per token: 120 requests or 60 writes per minute;
        per IP address: 30 failed authentications or 600 requests presenting a
        well-formed token per minute). Nothing was written. Carries
        `Retry-After: 60`, and `retry_after: 60` in the body: wait that long
        before retrying.
      headers:
        Cache-Control:
          $ref: '#/components/headers/CacheControl'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimitPolicy'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: rate_limited
              message: rate limited — retry after 60 seconds
              retry_after: 60
    Internal:
      description: An unexpected server error. The message is fixed and carries no detail.
      headers:
        Cache-Control:
          $ref: '#/components/headers/CacheControl'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimitPolicy'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            internal:
              value:
                error:
                  code: internal
                  message: internal error
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        A management token: `ds_mgmt_` followed by 40 lowercase hexadecimal
        characters, created by an owner in **Settings → Management tokens**.
        Opaque: send it as is, in the `Authorization` header only.

````