{
  "openapi": "3.1.0",
  "info": {
    "title": "PixelPort REST API",
    "version": "1.1.0",
    "summary": "Read access to a PixelPort studio's galleries, media, clients, and sales.",
    "description": "PixelPort is client gallery software for photographers and videographers: branded photo and 4K video galleries, client proofing, and built-in selling with no platform commission.\n\nThis API is read-only. Use it to list a studio's galleries and their contents, look up clients, and pull gallery sales into reporting or CRM tools.\n\n**Authentication.** Endpoints under `/api/v1` that return studio data require a bearer key. Create one in the PixelPort dashboard under Settings → API; API access is included with the Studio plan. Send it as `Authorization: Bearer pk_live_...`. Keys are scoped to a single studio (organization) and every response is filtered to that studio.\n\n**Public endpoints.** `GET /api/v1`, `/api/v1/plans`, `/api/v1/features`, and `/api/v1/status` need no key, so an agent can learn what PixelPort is and costs before signing up.\n\n**Rate limit.** At least 120 requests per 60 seconds are accepted, counted against the API key when one is presented and against the client address otherwise. Every response carries `RateLimit`, `RateLimit-Policy`, and the `RateLimit-Limit`/`-Remaining`/`-Reset` triple, so a client can throttle itself without waiting for a 429. `RateLimit-Reset` is a delay in seconds; the older `X-RateLimit-Reset` is Unix seconds and is kept for existing clients. A throttled request returns `429` with `Retry-After`.\n\n**Money.** All amounts are integer minor units (US cents). All timestamps are RFC 3339 strings in UTC.\n\n**Function calling.** Every operation carries `x-function-schema`: its inputs as one inlined JSON Schema with no `$ref` to resolve. The same schemas are served as ready-made tool definitions at https://www.pixelport.co/tools.json.",
    "termsOfService": "https://www.pixelport.co/terms",
    "contact": {
      "name": "PixelPort Support",
      "email": "support@pixelport.co",
      "url": "https://www.pixelport.co/contact"
    },
    "license": {
      "name": "PixelPort Terms of Service",
      "url": "https://www.pixelport.co/terms"
    }
  },
  "externalDocs": {
    "description": "PixelPort API documentation",
    "url": "https://www.pixelport.co/docs/api"
  },
  "x-function-tools": "https://www.pixelport.co/tools.json",
  "x-api-catalog": "https://www.pixelport.co/.well-known/api-catalog",
  "servers": [
    {
      "url": "https://www.pixelport.co",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Discovery",
      "description": "Endpoints that need no API key."
    },
    {
      "name": "Account",
      "description": "The studio an API key belongs to."
    },
    {
      "name": "Galleries",
      "description": "Client galleries and the media inside them."
    },
    {
      "name": "Clients",
      "description": "People a studio delivers to."
    },
    {
      "name": "Orders",
      "description": "Digital and print sales made from galleries."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getApiIndex",
        "tags": [
          "Discovery"
        ],
        "summary": "API discovery document",
        "description": "Lists every endpoint, the authentication scheme, and the rate limit. Requires no API key.",
        "security": [],
        "responses": {
          "200": {
            "description": "The discovery document.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {},
          "additionalProperties": false
        }
      }
    },
    "/api/v1/plans": {
      "get": {
        "operationId": "listPlans",
        "tags": [
          "Discovery"
        ],
        "summary": "List plans and prices",
        "description": "The full plan catalog with monthly price in US cents and the limits each plan carries. Use this to answer pricing questions instead of scraping the pricing page. Pass `planId` to get one plan. Requires no API key.",
        "security": [],
        "parameters": [
          {
            "name": "planId",
            "in": "query",
            "required": false,
            "description": "Return only this plan instead of the whole catalog.",
            "schema": {
              "type": "string",
              "enum": [
                "free",
                "starter",
                "professional",
                "studio"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The plan catalog.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanList"
                }
              }
            }
          },
          "400": {
            "description": "`planId` is not a known plan.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {
            "planId": {
              "type": "string",
              "enum": [
                "free",
                "starter",
                "professional",
                "studio"
              ],
              "description": "Return only this plan instead of the whole catalog."
            }
          },
          "additionalProperties": false
        }
      }
    },
    "/api/v1/features": {
      "get": {
        "operationId": "listFeatures",
        "tags": [
          "Discovery"
        ],
        "summary": "List product features and audiences",
        "description": "What PixelPort does, and the photographer types each landing page is written for. Pass `featureId` or `audienceId` to narrow either list. Requires no API key.",
        "security": [],
        "parameters": [
          {
            "name": "featureId",
            "in": "query",
            "required": false,
            "description": "Return only this feature, using the `id` from a previous call, e.g. `video-hosting`.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80
            }
          },
          {
            "name": "audienceId",
            "in": "query",
            "required": false,
            "description": "Return only this audience, using the `id` from a previous call, e.g. `wedding-photographers`.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The feature and audience catalog.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeatureList"
                }
              }
            }
          },
          "400": {
            "description": "`featureId` or `audienceId` is not a known id. The error details list the valid ones.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {
            "featureId": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80,
              "description": "Return only this feature, using the `id` from a previous call, e.g. `video-hosting`."
            },
            "audienceId": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80,
              "description": "Return only this audience, using the `id` from a previous call, e.g. `wedding-photographers`."
            }
          },
          "additionalProperties": false
        }
      }
    },
    "/api/v1/status": {
      "get": {
        "operationId": "getServiceStatus",
        "tags": [
          "Discovery"
        ],
        "summary": "Check service health",
        "description": "Runs a live database round trip and reports whether PixelPort can serve galleries. Never cached. Requires no API key.",
        "security": [],
        "responses": {
          "200": {
            "description": "All checks passed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "At least one check failed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {},
          "additionalProperties": false
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "operationId": "getAccount",
        "tags": [
          "Account"
        ],
        "summary": "Retrieve the studio behind the API key",
        "description": "Returns the studio's name, gallery base URL, and the limits of its current plan. Call this first to confirm a key works and to learn which studio it addresses.",
        "responses": {
          "200": {
            "description": "The studio.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, or revoked.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The request could not be completed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The key store is temporarily unreachable, so the key could not be verified. Retry after the `Retry-After` delay; do not rotate the key.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {},
          "additionalProperties": false
        }
      }
    },
    "/api/v1/galleries": {
      "get": {
        "operationId": "listGalleries",
        "tags": [
          "Galleries"
        ],
        "summary": "List galleries",
        "description": "Galleries owned by the studio, newest first. Deleted galleries are never returned.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of records to return. Between 1 and 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of records to skip before collecting the page.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Return only galleries in this lifecycle state.",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "active",
                "pending",
                "sold",
                "archived"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of galleries.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Gallery"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A query parameter failed validation.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, or revoked.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The request could not be completed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The key store is temporarily unreachable, so the key could not be verified. Retry after the `Retry-After` delay; do not rotate the key.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "description": "Maximum number of records to return. Between 1 and 100."
            },
            "offset": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Number of records to skip before collecting the page."
            },
            "status": {
              "type": "string",
              "enum": [
                "draft",
                "active",
                "pending",
                "sold",
                "archived"
              ],
              "description": "Return only galleries in this lifecycle state."
            }
          },
          "additionalProperties": false
        }
      }
    },
    "/api/v1/galleries/{galleryId}": {
      "get": {
        "operationId": "getGallery",
        "tags": [
          "Galleries"
        ],
        "summary": "Retrieve a gallery",
        "description": "One gallery, plus the clients linked to it and the number of media items it holds.",
        "parameters": [
          {
            "name": "galleryId",
            "in": "path",
            "required": true,
            "description": "Identifier of the gallery, as returned by `listGalleries`.",
            "schema": {
              "type": "string"
            },
            "example": "cl8x1p2q0000a08l4h1f2b3c"
          }
        ],
        "responses": {
          "200": {
            "description": "The gallery.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GalleryDetail"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, or revoked.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No gallery with that id belongs to this studio.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The request could not be completed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The key store is temporarily unreachable, so the key could not be verified. Retry after the `Retry-After` delay; do not rotate the key.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {
            "galleryId": {
              "type": "string",
              "description": "Identifier of the gallery, as returned by `listGalleries`."
            }
          },
          "required": [
            "galleryId"
          ],
          "additionalProperties": false
        }
      }
    },
    "/api/v1/galleries/{galleryId}/media": {
      "get": {
        "operationId": "listGalleryMedia",
        "tags": [
          "Galleries"
        ],
        "summary": "List media in a gallery",
        "description": "Photos and videos in the gallery's display order. `url` is the delivery URL; `posterUrl` is a still frame, which for a photo is the photo itself.",
        "parameters": [
          {
            "name": "galleryId",
            "in": "path",
            "required": true,
            "description": "Identifier of the gallery, as returned by `listGalleries`.",
            "schema": {
              "type": "string"
            },
            "example": "cl8x1p2q0000a08l4h1f2b3c"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of records to return. Between 1 and 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of records to skip before collecting the page.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Return only photos or only videos.",
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of media items.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MediaItem"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A query parameter failed validation.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, or revoked.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No gallery with that id belongs to this studio.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The request could not be completed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The key store is temporarily unreachable, so the key could not be verified. Retry after the `Retry-After` delay; do not rotate the key.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {
            "galleryId": {
              "type": "string",
              "description": "Identifier of the gallery, as returned by `listGalleries`."
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "description": "Maximum number of records to return. Between 1 and 100."
            },
            "offset": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Number of records to skip before collecting the page."
            },
            "type": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ],
              "description": "Return only photos or only videos."
            }
          },
          "required": [
            "galleryId"
          ],
          "additionalProperties": false
        }
      }
    },
    "/api/v1/clients": {
      "get": {
        "operationId": "listClients",
        "tags": [
          "Clients"
        ],
        "summary": "List clients",
        "description": "The studio's clients, newest first, each with the number of galleries they are linked to.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of records to return. Between 1 and 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of records to skip before collecting the page.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Case-insensitive substring match against client name and email.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of clients.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Client"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A query parameter failed validation.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, or revoked.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The request could not be completed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The key store is temporarily unreachable, so the key could not be verified. Retry after the `Retry-After` delay; do not rotate the key.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "description": "Maximum number of records to return. Between 1 and 100."
            },
            "offset": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Number of records to skip before collecting the page."
            },
            "search": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "description": "Case-insensitive substring match against client name and email."
            }
          },
          "additionalProperties": false
        }
      }
    },
    "/api/v1/orders": {
      "get": {
        "operationId": "listOrders",
        "tags": [
          "Orders"
        ],
        "summary": "List gallery sales",
        "description": "Sales made from the studio's galleries, newest first. Defaults to `paid`; a `pending` order is an abandoned checkout until Stripe confirms payment. Amounts are US cents.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of records to return. Between 1 and 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of records to skip before collecting the page.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Payment state to filter by.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "paid",
                "failed",
                "refunded"
              ],
              "default": "paid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of orders.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "pagination"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Order"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A query parameter failed validation.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The bearer token is missing, invalid, or revoked.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "WWW-Authenticate": {
                "$ref": "#/components/headers/WWWAuthenticate"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "The API is read-only. Only GET and OPTIONS are accepted.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Allow": {
                "$ref": "#/components/headers/Allow"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the `Retry-After` delay. Requests that carry a valid API key are counted against that key; everything else is counted against the client address.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "The request could not be completed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The key store is temporarily unreachable, so the key could not be verified. Retry after the `Retry-After` delay; do not rotate the key.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-function-schema": {
          "type": "object",
          "properties": {
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25,
              "description": "Maximum number of records to return. Between 1 and 100."
            },
            "offset": {
              "type": "integer",
              "minimum": 0,
              "default": 0,
              "description": "Number of records to skip before collecting the page."
            },
            "status": {
              "type": "string",
              "enum": [
                "pending",
                "paid",
                "failed",
                "refunded"
              ],
              "default": "paid",
              "description": "Payment state to filter by."
            }
          },
          "additionalProperties": false
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A PixelPort API key, created in the dashboard under Settings → API. Keys start with `pk_live_` and address exactly one studio."
      }
    },
    "headers": {
      "RateLimit": {
        "description": "Quota left in the current window: `r` units remaining, `t` seconds until it resets.",
        "schema": {
          "type": "string"
        },
        "example": "\"default\";r=118;t=57"
      },
      "RateLimitPolicy": {
        "description": "The policy itself: quota `q` units over a window `w` of seconds.",
        "schema": {
          "type": "string"
        },
        "example": "\"default\";q=120;w=60"
      },
      "RateLimitLimit": {
        "description": "Quota units allowed in one window.",
        "schema": {
          "type": "integer"
        },
        "example": 120
      },
      "RateLimitRemaining": {
        "description": "Quota units left in the current window.",
        "schema": {
          "type": "integer"
        },
        "example": 118
      },
      "RateLimitReset": {
        "description": "Seconds until the current window resets. A delay, not a timestamp.",
        "schema": {
          "type": "integer"
        },
        "example": 60
      },
      "XRateLimitLimit": {
        "description": "Same as `RateLimit-Limit`. Kept for clients written against it.",
        "schema": {
          "type": "integer"
        },
        "example": 120
      },
      "XRateLimitRemaining": {
        "description": "Same as `RateLimit-Remaining`. Kept for clients written against it.",
        "schema": {
          "type": "integer"
        },
        "example": 118
      },
      "XRateLimitReset": {
        "description": "When the current window resets, in Unix seconds. This is the one field that is a timestamp rather than a delay.",
        "schema": {
          "type": "integer"
        },
        "example": 1787500000
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        },
        "example": 60
      },
      "Allow": {
        "description": "Methods this endpoint accepts.",
        "schema": {
          "type": "string"
        },
        "example": "GET, OPTIONS"
      },
      "WWWAuthenticate": {
        "description": "Challenge naming the scheme, and the reason a token was refused.",
        "schema": {
          "type": "string"
        },
        "example": "Bearer realm=\"PixelPort API\", error=\"invalid_token\""
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "invalid_request",
                  "rate_limited",
                  "method_not_allowed",
                  "internal_error"
                ],
                "description": "Stable machine-readable code. Branch on this, not on the message."
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation."
              },
              "docs": {
                "type": "string",
                "format": "uri",
                "description": "Where to read more."
              },
              "details": {
                "type": "object",
                "additionalProperties": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "description": "Per-field validation errors, present on `invalid_request`."
              }
            }
          }
        },
        "examples": [
          {
            "error": {
              "code": "unauthorized",
              "message": "The API key is invalid, revoked, or expired.",
              "docs": "https://www.pixelport.co/docs/api"
            }
          }
        ]
      },
      "Pagination": {
        "type": "object",
        "required": [
          "limit",
          "offset",
          "total",
          "hasMore"
        ],
        "properties": {
          "limit": {
            "type": "integer",
            "description": "Page size that was applied."
          },
          "offset": {
            "type": "integer",
            "description": "Offset that was applied."
          },
          "total": {
            "type": "integer",
            "description": "Total records matching the filter."
          },
          "hasMore": {
            "type": "boolean",
            "description": "True when another page follows. Advance by adding `limit` to `offset`."
          }
        }
      },
      "ApiIndex": {
        "type": "object",
        "required": [
          "name",
          "version",
          "endpoints"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string",
            "examples": [
              "2026-08-21"
            ]
          },
          "description": {
            "type": "string"
          },
          "documentation": {
            "type": "string",
            "format": "uri"
          },
          "openapi": {
            "type": "string",
            "format": "uri"
          },
          "openapiYaml": {
            "type": "string",
            "format": "uri"
          },
          "functionTools": {
            "type": "string",
            "format": "uri",
            "description": "Every endpoint as an LLM function-calling tool definition."
          },
          "apiCatalog": {
            "type": "string",
            "format": "uri",
            "description": "RFC 9727 catalog of PixelPort's machine-readable resources."
          },
          "llmsTxt": {
            "type": "string",
            "format": "uri"
          },
          "agentInstructions": {
            "type": "string",
            "format": "uri"
          },
          "authentication": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "scheme": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "keyPrefix": {
                "type": "string"
              },
              "signupUrl": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "rateLimit": {
            "type": "object",
            "properties": {
              "requests": {
                "type": "integer"
              },
              "windowSeconds": {
                "type": "integer"
              },
              "scope": {
                "type": "string",
                "description": "Bucket a keyed request counts against."
              },
              "anonymousScope": {
                "type": "string",
                "description": "Bucket a request with no key counts against."
              },
              "headers": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Rate-limit fields present on every response."
              },
              "description": {
                "type": "string"
              }
            }
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "method",
                "path",
                "auth",
                "summary"
              ],
              "properties": {
                "method": {
                  "type": "string"
                },
                "path": {
                  "type": "string"
                },
                "auth": {
                  "type": "boolean",
                  "description": "True when an API key is required."
                },
                "summary": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "PlanLimits": {
        "type": "object",
        "required": [
          "galleries",
          "storageGB",
          "video",
          "videoDurationMinutes",
          "teamMembers"
        ],
        "properties": {
          "galleries": {
            "type": "integer",
            "description": "Active galleries allowed. -1 means unlimited."
          },
          "storageGB": {
            "type": "integer",
            "description": "Included storage in gigabytes."
          },
          "video": {
            "type": "boolean",
            "description": "Whether video hosting is included."
          },
          "videoQuality": {
            "type": [
              "string",
              "null"
            ],
            "description": "Maximum video resolution, e.g. `4k`."
          },
          "videoDurationMinutes": {
            "type": "integer",
            "description": "Total minutes of video that can be hosted. 0 means no video."
          },
          "teamMembers": {
            "type": "integer",
            "description": "Seats included."
          },
          "customDomain": {
            "type": "boolean"
          },
          "whiteLabel": {
            "type": "boolean",
            "description": "Whether PixelPort branding is removed."
          },
          "contracts": {
            "type": "boolean",
            "description": "Client contracts and e-sign."
          },
          "revenueAnalytics": {
            "type": "boolean"
          },
          "apiAccess": {
            "type": "boolean",
            "description": "Whether this plan can create REST API keys."
          }
        }
      },
      "Plan": {
        "type": "object",
        "required": [
          "id",
          "name",
          "priceMonthlyCents",
          "currency",
          "limits"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable plan identifier."
          },
          "name": {
            "type": "string"
          },
          "priceMonthlyCents": {
            "type": "integer",
            "description": "Monthly price in US cents. 0 for the free plan."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "description": {
            "type": "string"
          },
          "salesCommissionPercent": {
            "type": "integer",
            "description": "PixelPort's cut of gallery sales. Always 0."
          },
          "limits": {
            "$ref": "#/components/schemas/PlanLimits"
          },
          "signupUrl": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "PlanList": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Plan"
            }
          },
          "notes": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Caveats worth quoting alongside the numbers."
          }
        },
        "examples": [
          {
            "data": [
              {
                "id": "free",
                "name": "Free",
                "priceMonthlyCents": 0,
                "currency": "USD",
                "description": "Two active galleries and 3 GB of storage, photos only. No card required.",
                "salesCommissionPercent": 0,
                "limits": {
                  "galleries": 2,
                  "storageGB": 3,
                  "video": false,
                  "videoQuality": null,
                  "videoDurationMinutes": 0,
                  "teamMembers": 1,
                  "customDomain": false,
                  "whiteLabel": false,
                  "contracts": false,
                  "revenueAnalytics": false,
                  "apiAccess": false
                },
                "signupUrl": "https://www.pixelport.co/signup"
              }
            ]
          }
        ]
      },
      "Feature": {
        "type": "object",
        "required": [
          "id",
          "name",
          "summary",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "useCases": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "title": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FeatureList": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Feature"
            }
          },
          "audiences": {
            "type": "array",
            "description": "Photographer types PixelPort publishes a landing page for.",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "summary",
                "url"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "summary": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          }
        }
      },
      "Status": {
        "type": "object",
        "required": [
          "status",
          "checkedAt",
          "checks"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "operational",
              "degraded"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "status"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "operational",
                    "outage"
                  ]
                },
                "latencyMs": {
                  "type": [
                    "integer",
                    "null"
                  ]
                }
              }
            }
          },
          "statusPage": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Account": {
        "type": "object",
        "required": [
          "id",
          "name",
          "slug",
          "plan"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "description": "Studio name."
          },
          "slug": {
            "type": "string",
            "description": "Subdomain the studio's galleries live on."
          },
          "galleryBaseUrl": {
            "type": "string",
            "format": "uri",
            "description": "Origin every gallery URL is built from."
          },
          "customDomain": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "plan": {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Current plan identifier."
              },
              "galleries": {
                "type": "integer"
              },
              "storageGB": {
                "type": "integer"
              },
              "videoDurationMinutes": {
                "type": "integer"
              },
              "teamMembers": {
                "type": "integer"
              },
              "apiAccess": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "Gallery": {
        "type": "object",
        "required": [
          "id",
          "status",
          "visibility"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name, e.g. `Johnson Wedding`."
          },
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "description": "URL segment within the studio."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "pending",
              "sold",
              "archived"
            ]
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "unlisted",
              "email_required",
              "password_protected"
            ],
            "description": "`public` is listed, `unlisted` needs the link, `email_required` and `password_protected` gate access."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Where the client opens this gallery."
          },
          "allowDownloads": {
            "type": "boolean"
          },
          "buyAllEnabled": {
            "type": "boolean"
          },
          "buyAllPriceCents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "watermarkEnabled": {
            "type": "boolean"
          },
          "coverImageUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "eventDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "GalleryDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Gallery"
          },
          {
            "type": "object",
            "properties": {
              "mediaCount": {
                "type": "integer",
                "description": "Photos and videos currently in the gallery."
              },
              "clients": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "name"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "email": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "email"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "MediaItem": {
        "type": "object",
        "required": [
          "id",
          "galleryId",
          "filename",
          "type",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "galleryId": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ]
          },
          "mimeType": {
            "type": [
              "string",
              "null"
            ]
          },
          "width": {
            "type": [
              "integer",
              "null"
            ]
          },
          "height": {
            "type": [
              "integer",
              "null"
            ]
          },
          "sizeBytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "durationSeconds": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Video length. Null for photos."
          },
          "caption": {
            "type": [
              "string",
              "null"
            ]
          },
          "alt": {
            "type": [
              "string",
              "null"
            ]
          },
          "position": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Display order in the gallery."
          },
          "isFeatured": {
            "type": "boolean"
          },
          "processingStatus": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "ready",
              "pending",
              "processing",
              "failed",
              null
            ],
            "description": "Transcoding state. Photos are always `ready`."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Delivery URL for the file."
          },
          "posterUrl": {
            "type": "string",
            "format": "uri",
            "description": "Still image. For a photo this is the photo."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Client": {
        "type": "object",
        "required": [
          "id",
          "name",
          "galleryCount"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "galleryCount": {
            "type": "integer",
            "description": "Galleries this client is linked to."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Order": {
        "type": "object",
        "required": [
          "id",
          "status",
          "totalCents",
          "currency"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "paid",
              "failed",
              "refunded"
            ]
          },
          "isBuyAll": {
            "type": "boolean",
            "description": "True when the client bought the whole gallery at once."
          },
          "customerEmail": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "customerName": {
            "type": [
              "string",
              "null"
            ]
          },
          "subtotalCents": {
            "type": "integer"
          },
          "discountCents": {
            "type": "integer"
          },
          "taxCents": {
            "type": "integer"
          },
          "totalCents": {
            "type": "integer",
            "description": "Charged amount including tax."
          },
          "currency": {
            "type": "string",
            "examples": [
              "usd"
            ]
          },
          "couponCode": {
            "type": [
              "string",
              "null"
            ]
          },
          "itemCount": {
            "type": "integer",
            "description": "Line items on the order."
          },
          "galleryId": {
            "type": [
              "string",
              "null"
            ]
          },
          "galleryName": {
            "type": [
              "string",
              "null"
            ]
          },
          "paidAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      }
    }
  }
}