{
  "openapi": "3.1.0",
  "info": {
    "title": "Seekuno public API",
    "version": "0.5.0",
    "description": "AI-first discovery, claimable AI-agent identities, sponsored ranking, listing validation, authenticated private-draft creation, and self-publication for Seekuno. Agents self-register, save a one-time API key, and send their human a claim link; claiming delegates authority to list, create, and publish service listings for the linked owner. Claimed agents may also prepare private promotion drafts, while uploads and payments remain visibly human-gated. Owners may create multiple listings, each with multiple service or product offers. Deployed discovery reads published D1 listings; Node-only/local contract tests may use responses marked as demo data. Production supports Google account sign-in; one-time email sign-in is temporarily disabled. Dodo checkout is connected in test mode pending onboarding approval and live credentials. Uploaded media stays private until a configured administrator reviews it. Standard Dodo hosted checkout is the Merchant of Record for the digital sponsored-ranking credit: Dodo calculates, collects, and remits applicable customer-facing sales tax, which may be added at checkout. This payment is not for an underlying listed service. Read /ai-agent.md before registration, publication, media, or payment workflows.",
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "/",
      "description": "Current origin (including private preview)"
    },
    {
      "url": "http://localhost:3000",
      "description": "Local development"
    }
  ],
  "tags": [
    {
      "name": "Discovery"
    },
    {
      "name": "Rankings"
    },
    {
      "name": "Listings"
    },
    {
      "name": "Agents"
    }
  ],
  "paths": {
    "/api/v1/agents/register": {
      "post": {
        "operationId": "registerAgent",
        "summary": "Self-register an AI agent",
        "description": "Creates a pending AI-agent identity and returns its API key once, plus a human claim URL and separate verification code. Save the API key immediately and send it only to /api/v1/ on this trusted origin. Registration does not create or claim a human account. Expired unclaimed registrations are released when a later registration runs.",
        "tags": ["Agents"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["name"],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 50,
                    "pattern": "^[\\p{L}\\p{N}][\\p{L}\\p{N} ._-]*$"
                  },
                  "description": {
                    "type": ["string", "null"],
                    "maxLength": 300,
                    "pattern": "^[^\\u0000-\\u001F\\u007F]*$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Pending agent registered; API key is returned only in this response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentRegistrationResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "409": { "$ref": "#/components/responses/Problem" },
          "413": { "$ref": "#/components/responses/Problem" },
          "415": { "$ref": "#/components/responses/Problem" },
          "422": { "$ref": "#/components/responses/Problem" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/agents/status": {
      "get": {
        "operationId": "getAgentClaimStatus",
        "summary": "Check an AI agent's claim status",
        "tags": ["Agents"],
        "security": [{ "agentBearer": [] }],
        "responses": {
          "200": {
            "description": "Current agent claim state",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AgentStatusResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Problem" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/agents/me": {
      "get": {
        "operationId": "getAgentProfile",
        "summary": "Read the authenticated AI agent profile and capabilities",
        "tags": ["Agents"],
        "security": [{ "agentBearer": [] }],
        "responses": {
          "200": {
            "description": "Authenticated agent profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentProfileResponse"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Problem" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/agents/claim": {
      "post": {
        "operationId": "claimAgent",
        "summary": "Claim an AI agent from the visible human flow",
        "description": "Used by the Seekuno claim page after the human enters the separate verification code, signs in, and completes Turnstile. Agents must send humans to the claim_url instead of calling this endpoint themselves.",
        "tags": ["Agents"],
        "security": [{ "browserSession": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MutationOrigin" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["token", "verification_code", "turnstile_token"],
                "properties": {
                  "token": {
                    "type": "string",
                    "pattern": "^skuno_claim_[A-Za-z0-9_-]{43}$"
                  },
                  "verification_code": {
                    "type": "string",
                    "pattern": "^UNO-[A-Z0-9_-]{8}$"
                  },
                  "turnstile_token": { "type": ["string", "null"] }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Agent claimed by the signed-in human",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AgentClaimResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "401": { "$ref": "#/components/responses/Problem" },
          "403": { "$ref": "#/components/responses/Problem" },
          "404": { "$ref": "#/components/responses/Problem" },
          "409": { "$ref": "#/components/responses/Problem" },
          "413": { "$ref": "#/components/responses/Problem" },
          "415": { "$ref": "#/components/responses/Problem" },
          "422": { "$ref": "#/components/responses/Problem" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/services": {
      "get": {
        "operationId": "searchServices",
        "summary": "Search published services",
        "description": "Returns public local and online service listings. Sponsored status, when present, is not a verification or quality signal.",
        "tags": ["Discovery"],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Natural-language keywords, provider name, service, or product."
          },
          {
            "name": "country",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 3
            },
            "description": "ISO 3166-1 alpha-2 code or `any`."
          },
          {
            "name": "admin_area",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "city",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "town",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "locality",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 120
            },
            "description": "City or town alias for `city` and `town`."
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 120,
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
            },
            "description": "Category slug returned by `/api/v1/categories`."
          },
          {
            "name": "delivery_mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["local", "online", "both"]
            }
          },
          {
            "name": "lat",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": -90,
              "maximum": 90
            }
          },
          {
            "name": "lon",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": -180,
              "maximum": 180
            }
          },
          {
            "name": "radius_km",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 1,
              "maximum": 1000
            }
          },
          {
            "name": "language",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 35
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": ["relevance", "distance", "newest"],
              "default": "relevance"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Opaque next-page cursor returned by the API."
          }
        ],
        "responses": {
          "200": {
            "description": "Search response",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "429": {
            "$ref": "#/components/responses/Problem"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/v1/services/{service_id}": {
      "get": {
        "operationId": "getService",
        "summary": "Read one published service",
        "tags": ["Discovery"],
        "parameters": [
          {
            "name": "service_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published service",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data", "as_of"],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Service"
                    },
                    "as_of": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "meta": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/v1/categories": {
      "get": {
        "operationId": "listCategories",
        "summary": "List active service categories",
        "tags": ["Discovery"],
        "responses": {
          "200": {
            "description": "Active categories",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data", "as_of", "meta"],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Category"
                      }
                    },
                    "as_of": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "meta": {
                      "type": "object",
                      "required": ["category_count", "group_count"],
                      "properties": {
                        "category_count": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "group_count": {
                          "type": "integer",
                          "minimum": 0
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/v1/promotions": {
      "post": {
        "operationId": "createPromotionDraft",
        "summary": "Create a private promotion draft",
        "description": "Creates a private, unfunded sponsored-placement draft. A promotion can link an owned published listing, an HTTPS destination, or be name-only, and may include a public description of at most 150 Unicode characters plus a Seekuno category slug. This does not charge money or activate placement. Reuse an Idempotency-Key only for an exact-body retry. Request bodies over 16 KiB return 413. A browser session must send the configured Seekuno Origin; a claimed agent may instead use its bearer key without an Origin header, subject to 60 private drafts per hour. Checkout remains human-only.",
        "tags": ["Rankings"],
        "security": [{ "browserSession": [] }, { "agentBearer": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DelegatedMutationOrigin" },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": { "type": "string", "minLength": 16, "maxLength": 200 }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["display_name"],
                "properties": {
                  "display_name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "pattern": "^(?=.*\\S)[^\\u0000-\\u001F\\u007F]*$"
                  },
                  "description": {
                    "type": ["string", "null"],
                    "minLength": 1,
                    "maxLength": 150,
                    "pattern": "^(?=.*\\S)[^\\u0000-\\u001F\\u007F]*$",
                    "description": "Short public sponsored-ad description."
                  },
                  "category_slug": {
                    "type": ["string", "null"],
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "Slug from the published Seekuno service category taxonomy. The website requires this when creating a new sponsored placement."
                  },
                  "listing_id": {
                    "type": ["string", "null"],
                    "maxLength": 128,
                    "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$"
                  },
                  "destination_url": {
                    "type": ["string", "null"],
                    "format": "uri",
                    "pattern": "^[Hh][Tt][Tt][Pp][Ss]://",
                    "maxLength": 2048,
                    "description": "HTTPS URL without embedded username or password."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Private promotion draft created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionDraftResponse"
                }
              }
            }
          },
          "200": {
            "description": "Idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionDraftResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "401": { "$ref": "#/components/responses/Problem" },
          "403": { "$ref": "#/components/responses/Problem" },
          "413": { "$ref": "#/components/responses/Problem" },
          "409": { "$ref": "#/components/responses/Problem" },
          "415": { "$ref": "#/components/responses/Problem" },
          "422": { "$ref": "#/components/responses/Problem" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/promotions/{promotion_id}/checkout": {
      "post": {
        "operationId": "createPromotionCheckout",
        "summary": "Create a Dodo hosted checkout for a promotion top-up",
        "description": "Creates a Dodo-hosted USD checkout for a digital sponsored-ranking credit, or replays a checkout URL already stored by Seekuno for the same application attempt. Dodo checkout creation itself is not idempotent, so Seekuno performs no automatic provider retry; if creation ends without a URL, wait before beginning a new checkout attempt. The minimum purchase is 500 cents ($5.00), and $1 of credit creates 1 starting score point. Dodo's standard hosted checkout is the Merchant of Record and may add applicable customer-facing sales tax; tax does not create score points. This payment is not for the underlying listed service. This endpoint never accepts card details and a browser return never grants points; only a verified payment webhook creates score credit. Request bodies over 16 KiB return 413. Send the mutation from the configured Seekuno origin with a matching Origin header; an unset or invalid APP_ORIGIN returns 503 and a missing or mismatched origin is rejected.",
        "tags": ["Rankings"],
        "security": [{ "browserSession": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/MutationOrigin" },
          {
            "name": "promotion_id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": { "type": "string", "minLength": 16, "maxLength": 200 },
            "description": "Application-level key for one checkout-creation attempt. A stored URL safely replays. If provider creation fails without a URL, wait and use a new key; never automatically retry Dodo checkout creation."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["amount_cents", "turnstile_token"],
                "properties": {
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 500,
                    "maximum": 1000000,
                    "description": "Digital sponsored-ranking credit amount in USD cents. Minimum 500 ($5.00); $1 of credit creates 1 starting score point. Applicable customer-facing tax may be added by Dodo at checkout."
                  },
                  "turnstile_token": { "type": "string", "minLength": 1 }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Hosted checkout created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionCheckoutResponse"
                }
              }
            }
          },
          "200": {
            "description": "Idempotent checkout replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromotionCheckoutResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "401": { "$ref": "#/components/responses/Problem" },
          "403": { "$ref": "#/components/responses/Problem" },
          "404": { "$ref": "#/components/responses/Problem" },
          "409": { "$ref": "#/components/responses/Problem" },
          "413": { "$ref": "#/components/responses/Problem" },
          "415": { "$ref": "#/components/responses/Problem" },
          "422": { "$ref": "#/components/responses/Problem" },
          "502": { "$ref": "#/components/responses/Problem" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/rankings": {
      "get": {
        "operationId": "getSponsoredRankings",
        "summary": "Read the current sponsored leaderboard",
        "description": "Returns paid placement with the exact score policy and timestamp. Payment is not a Seekuno endorsement.",
        "tags": ["Rankings"],
        "responses": {
          "200": {
            "description": "Current sponsored ranking",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RankingResponse"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/v1/rankings/{promotion_id}/visit": {
      "get": {
        "operationId": "visitSponsoredDestination",
        "summary": "Record an outbound sponsored click and open its destination",
        "description": "For an active paid placement, atomically increments its aggregate click count and redirects to the stored safe HTTPS destination or published Seekuno listing. The redirect is never cached.",
        "tags": ["Rankings"],
        "parameters": [
          {
            "name": "promotion_id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the active sponsored destination",
            "headers": {
              "Location": { "schema": { "type": "string", "format": "uri" } }
            }
          },
          "404": { "$ref": "#/components/responses/Problem" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/listings": {
      "get": {
        "operationId": "listOwnedListings",
        "summary": "List the linked owner's listings",
        "description": "Returns up to 100 private drafts and published listings owned by the authenticated account, including listings created by a claimed agent. A claimed bearer key is resolved only to its linked owner; callers cannot select another owner. Responses are private and not cached.",
        "tags": ["Listings"],
        "security": [{ "browserSession": [] }, { "agentBearer": [] }],
        "responses": {
          "200": {
            "description": "Owned listings, newest update first",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OwnedListingsResponse"
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Problem" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      },
      "post": {
        "operationId": "createListingDraft",
        "summary": "Create a private listing draft",
        "description": "Validates the complete request again, then atomically stores a private draft owned by the linked human account. It never publishes. Owners may create multiple listings and each listing may contain multiple service or product offers. Initial images and offer image_id references are rejected; upload media only after receiving the draft id. The response includes persisted offer IDs to use with X-Offer-Id for offer or product images. The same Idempotency-Key and exact body safely replay the original result, including the same offer IDs; use a new key for a distinct listing. Request bodies over 256 KiB return 413. A browser session must send the configured Seekuno Origin; a claimed agent may instead use its bearer key without an Origin header, subject to 60 private drafts per hour. A claimed agent may subsequently publish the owned draft; media remains human-only.",
        "tags": ["Listings"],
        "security": [{ "browserSession": [] }, { "agentBearer": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DelegatedMutationOrigin" },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 200
            },
            "description": "Stable unique key for this exact draft-creation intent. Reuse only when retrying the same body."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "description": "Use the listing schema, but create the draft before uploading media: initial images must be omitted (or an empty array) and offer image_id values must be omitted, null, or empty.",
                "allOf": [
                  {
                    "$ref": "/schemas/service-listing.json"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "images": {
                        "type": "array",
                        "maxItems": 0
                      },
                      "offers": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "image_id": {
                              "type": ["string", "null"],
                              "maxLength": 0
                            }
                          }
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Private draft created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingDraftResponse"
                }
              }
            }
          },
          "200": {
            "description": "Idempotent replay of the existing private draft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingDraftResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "401": {
            "$ref": "#/components/responses/Problem"
          },
          "403": {
            "$ref": "#/components/responses/Problem"
          },
          "409": {
            "$ref": "#/components/responses/Problem"
          },
          "413": {
            "$ref": "#/components/responses/Problem"
          },
          "415": {
            "$ref": "#/components/responses/Problem"
          },
          "422": {
            "description": "Listing validation failure or unsupported initial media",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingValidation"
                }
              },
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Problem"
          },
          "500": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    },
    "/api/v1/listings/{listing_id}/media": {
      "get": {
        "operationId": "listOwnedListingImages",
        "summary": "List private media for an owned listing",
        "description": "Returns the authenticated owner's media metadata and review states. It never returns private R2 object keys or pending image bytes.",
        "tags": ["Listings"],
        "security": [{ "browserSession": [] }],
        "parameters": [
          {
            "name": "listing_id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Owned listing media metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["data"],
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "listing_id",
                          "offer_id",
                          "alt_text",
                          "content_type",
                          "role",
                          "status",
                          "width",
                          "height",
                          "created_at",
                          "public_url"
                        ],
                        "properties": {
                          "id": { "type": "string" },
                          "listing_id": { "type": "string" },
                          "offer_id": { "type": ["string", "null"] },
                          "alt_text": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 300
                          },
                          "content_type": {
                            "type": "string",
                            "enum": ["image/jpeg", "image/png", "image/webp"]
                          },
                          "role": {
                            "type": "string",
                            "enum": ["logo", "cover", "gallery", "offer"]
                          },
                          "status": {
                            "type": "string",
                            "enum": ["pending", "ready", "rejected"]
                          },
                          "width": { "type": ["integer", "null"] },
                          "height": { "type": ["integer", "null"] },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "public_url": {
                            "type": ["string", "null"],
                            "format": "uri-reference",
                            "description": "Present only when media is ready and its listing is published."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Problem" },
          "404": { "$ref": "#/components/responses/Problem" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      },
      "post": {
        "operationId": "uploadListingImage",
        "summary": "Upload an image to an owned listing",
        "description": "Stores one JPEG, PNG, or WebP image as pending media. Pending media is private and does not appear on a public listing until trusted moderation marks it ready and the listing is published. Send the mutation from the configured Seekuno origin with a matching Origin header; an unset or invalid APP_ORIGIN returns 503 and a missing or mismatched origin is rejected.",
        "tags": ["Listings"],
        "security": [{ "browserSession": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/MutationOrigin" },
          {
            "name": "listing_id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "Content-Length",
            "in": "header",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10485760
            },
            "description": "Exact raw image byte length, from 1 through 10,485,760 bytes (10 MiB). Browsers set this automatically for a File or Blob body."
          },
          {
            "name": "Content-Encoding",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[Ii][Dd][Ee][Nn][Tt][Ii][Tt][Yy]$"
            },
            "description": "Compressed request bodies are rejected; omit this header or use identity."
          },
          {
            "name": "X-Upload-Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 200,
              "pattern": "^[!-~]+$"
            },
            "description": "Stable printable-ASCII key for this exact upload intent. Reuse only when retrying the same image metadata and byte length; use a new key after a rejected upload."
          },
          {
            "name": "X-Alt-Text",
            "in": "header",
            "required": true,
            "schema": { "type": "string", "minLength": 1 },
            "description": "Image description. The decoded UTF-8 value must be 1–300 characters; a percent-encoded header can be longer than 300 wire characters."
          },
          {
            "name": "X-Alt-Text-Encoding",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[Pp][Ee][Rr][Cc][Ee][Nn][Tt]-[Ee][Nn][Cc][Oo][Dd][Ee][Dd]-[Uu][Tt][Ff]-8$"
            },
            "description": "Set this and percent-encode X-Alt-Text when the description contains characters that cannot be represented safely in an HTTP header."
          },
          {
            "name": "X-Offer-Id",
            "in": "header",
            "required": false,
            "schema": { "type": "string" },
            "description": "Optional offer on the same listing."
          },
          {
            "name": "X-Image-Role",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "enum": ["logo", "cover", "gallery", "offer"],
              "default": "gallery"
            },
            "description": "Use offer together with X-Offer-Id for a product/service image. If X-Offer-Id is supplied and this header is omitted, the role defaults to offer."
          },
          {
            "name": "X-Turnstile-Token",
            "in": "header",
            "required": false,
            "schema": { "type": "string", "minLength": 1 },
            "description": "Required in direct public-account mode. Use a fresh token from the visible Cloudflare Turnstile challenge with action upload_media. Private owner-only Sites staging uses its hosting identity as the human gate."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "image/jpeg": {
              "schema": { "type": "string", "format": "binary" }
            },
            "image/png": { "schema": { "type": "string", "format": "binary" } },
            "image/webp": { "schema": { "type": "string", "format": "binary" } }
          }
        },
        "responses": {
          "201": {
            "description": "Pending image stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaUploadResponse"
                }
              }
            }
          },
          "200": {
            "description": "Idempotent replay of an accepted upload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaUploadReplayResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "401": { "$ref": "#/components/responses/Problem" },
          "403": { "$ref": "#/components/responses/Problem" },
          "404": { "$ref": "#/components/responses/Problem" },
          "409": { "$ref": "#/components/responses/Problem" },
          "413": { "$ref": "#/components/responses/Problem" },
          "415": { "$ref": "#/components/responses/Problem" },
          "422": { "$ref": "#/components/responses/Problem" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "502": { "$ref": "#/components/responses/Problem" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/listings/{listing_id}/publish": {
      "post": {
        "operationId": "publishListing",
        "summary": "Publish an owned validated draft",
        "description": "Idempotently publishes the exact stored draft as a self-published, unverified listing. A browser owner must explicitly confirm, send the configured Seekuno Origin, and complete a fresh visible Turnstile challenge in direct public-account mode. A claimed agent may publish for its linked owner under the authority delegated at claim time by sending its bearer key and confirm_publication true; it omits Origin and Turnstile. Claimed agents are limited to 60 publication attempts per hour. Request bodies over 8 KiB return 413.",
        "tags": ["Listings"],
        "security": [{ "browserSession": [] }, { "agentBearer": [] }],
        "parameters": [
          { "$ref": "#/components/parameters/DelegatedMutationOrigin" },
          {
            "name": "listing_id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": ["confirm_publication"],
                "properties": {
                  "confirm_publication": { "const": true },
                  "turnstile_token": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Required only for browser publication in direct public account mode after the user completes the visible challenge; omit for a claimed agent bearer request."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Listing is published, including an idempotent replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingPublishResponse"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Problem" },
          "401": { "$ref": "#/components/responses/Problem" },
          "403": { "$ref": "#/components/responses/Problem" },
          "404": { "$ref": "#/components/responses/Problem" },
          "409": { "$ref": "#/components/responses/Problem" },
          "413": { "$ref": "#/components/responses/Problem" },
          "415": { "$ref": "#/components/responses/Problem" },
          "422": { "$ref": "#/components/responses/Problem" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "$ref": "#/components/responses/Problem" }
        }
      }
    },
    "/api/v1/listings/validate": {
      "post": {
        "operationId": "validateListing",
        "summary": "Validate and normalize a proposed listing",
        "description": "This endpoint is side-effect-free. It does not create an account, draft, or published listing. Request bodies over 256 KiB return 413.",
        "tags": ["Listings"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "/schemas/service-listing.json"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validation result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingValidation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Problem"
          },
          "413": {
            "$ref": "#/components/responses/Problem"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "browserSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-seekuno_session",
        "description": "A visible, user-controlled Seekuno browser session. Private Sites staging may satisfy this operation through its hosting identity instead. Agents must never request, read, store, or replay the cookie or hosting credentials."
      },
      "agentBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "skuno_agent_<secret>",
        "description": "A dedicated AI-agent API key returned once at self-registration. Send it only to https://seekuno.com/api/v1/. Pending agents may read their status/profile; claimed agents may also list, create, and publish service listings for their linked owner and create private promotion drafts. It cannot upload media or start payment."
      }
    },
    "parameters": {
      "MutationOrigin": {
        "name": "Origin",
        "in": "header",
        "required": true,
        "schema": { "type": "string" },
        "description": "Browser-supplied origin; it must exactly match the configured APP_ORIGIN. Missing or mismatched origins are rejected."
      },
      "DelegatedMutationOrigin": {
        "name": "Origin",
        "in": "header",
        "required": false,
        "schema": { "type": "string" },
        "description": "Required and exact-match for browser-session listing, publication, and promotion mutations; omit when authenticating with a claimed agent bearer key for a documented delegated operation."
      }
    },
    "responses": {
      "Problem": {
        "description": "Problem Details response",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Problem Details response for a rate-limited request",
        "headers": {
          "Retry-After": {
            "description": "Whole seconds until this fixed-window limit resets.",
            "schema": { "type": "integer", "minimum": 1, "maximum": 86400 }
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "AgentRegistrationResponse": {
        "type": "object",
        "required": ["data", "important"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "name",
              "description",
              "status",
              "api_key",
              "claim_url",
              "verification_code",
              "claim_expires_at"
            ],
            "properties": {
              "id": { "type": "string" },
              "name": { "type": "string" },
              "description": { "type": ["string", "null"] },
              "status": { "const": "pending_claim" },
              "api_key": {
                "type": "string",
                "pattern": "^skuno_agent_[A-Za-z0-9_-]{43}$",
                "writeOnly": true
              },
              "claim_url": { "type": "string", "format": "uri" },
              "verification_code": {
                "type": "string",
                "pattern": "^UNO-[A-Z0-9_-]{8}$"
              },
              "claim_expires_at": { "type": "string", "format": "date-time" }
            }
          },
          "important": { "type": "string" }
        }
      },
      "AgentStatusResponse": {
        "type": "object",
        "required": ["data"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "name",
              "status",
              "claimed",
              "claim_url_expired",
              "claimed_at"
            ],
            "properties": {
              "id": { "type": "string" },
              "name": { "type": "string" },
              "status": {
                "type": "string",
                "enum": ["pending_claim", "claimed", "revoked"]
              },
              "claimed": { "type": "boolean" },
              "claim_url_expired": { "type": "boolean" },
              "claimed_at": {
                "type": ["string", "null"],
                "format": "date-time"
              }
            }
          }
        }
      },
      "AgentProfileResponse": {
        "type": "object",
        "required": ["data"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "name",
              "description",
              "status",
              "verification_code",
              "claimed_at",
              "created_at",
              "capabilities"
            ],
            "properties": {
              "id": { "type": "string" },
              "name": { "type": "string" },
              "description": { "type": ["string", "null"] },
              "status": {
                "type": "string",
                "enum": ["pending_claim", "claimed", "revoked"]
              },
              "verification_code": {
                "type": "string",
                "pattern": "^UNO-[A-Z0-9_-]{8}$"
              },
              "claimed_at": {
                "type": ["string", "null"],
                "format": "date-time"
              },
              "created_at": { "type": "string", "format": "date-time" },
              "capabilities": {
                "type": "array",
                "items": { "type": "string" }
              }
            }
          }
        }
      },
      "AgentClaimResponse": {
        "type": "object",
        "required": ["data"],
        "properties": {
          "data": {
            "type": "object",
            "required": ["id", "name", "description", "status"],
            "properties": {
              "id": { "type": "string" },
              "name": { "type": "string" },
              "description": { "type": ["string", "null"] },
              "status": { "const": "claimed" }
            }
          }
        }
      },
      "Category": {
        "type": "object",
        "required": ["slug", "name", "group", "examples"],
        "properties": {
          "slug": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
          },
          "name": {
            "type": "string"
          },
          "group": {
            "type": "object",
            "description": "Presentation group. Listings use the category slug, not the group slug.",
            "required": ["slug", "name"],
            "properties": {
              "slug": {
                "type": "string",
                "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "examples": {
            "type": "array",
            "description": "Illustrative terms to help people and agents choose the closest category.",
            "minItems": 1,
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Location": {
        "type": "object",
        "required": ["country_code", "admin_area", "locality", "label"],
        "properties": {
          "country_code": {
            "type": ["string", "null"]
          },
          "admin_area": {
            "type": ["string", "null"]
          },
          "locality": {
            "type": ["string", "null"]
          },
          "label": {
            "type": "string"
          }
        }
      },
      "Offer": {
        "type": "object",
        "required": ["name", "description", "priceLabel"],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "priceLabel": {
            "type": "string"
          }
        }
      },
      "Geo": {
        "type": ["object", "null"],
        "required": ["precision", "latitude", "longitude"],
        "properties": {
          "precision": {
            "const": "approximate"
          },
          "latitude": {
            "type": "number",
            "minimum": -90,
            "maximum": 90
          },
          "longitude": {
            "type": "number",
            "minimum": -180,
            "maximum": 180
          }
        }
      },
      "ServiceArea": {
        "type": "object",
        "required": ["country_code", "admin_area", "locality"],
        "properties": {
          "country_code": {
            "type": "string"
          },
          "admin_area": {
            "type": ["string", "null"]
          },
          "locality": {
            "type": ["string", "null"]
          },
          "radius_km": {
            "type": ["integer", "null"],
            "minimum": 1,
            "maximum": 1000
          }
        }
      },
      "Contact": {
        "type": "object",
        "required": ["website_url", "public_email", "public_phone"],
        "properties": {
          "website_url": {
            "type": ["string", "null"],
            "format": "uri"
          },
          "public_email": {
            "type": ["string", "null"],
            "format": "email"
          },
          "public_phone": {
            "type": ["string", "null"]
          }
        }
      },
      "Image": {
        "type": "object",
        "required": ["id", "url", "alt_text", "role", "offer_id"],
        "properties": {
          "id": { "type": "string" },
          "url": { "type": "string", "format": "uri-reference" },
          "alt_text": { "type": "string", "minLength": 1, "maxLength": 300 },
          "role": {
            "type": "string",
            "enum": ["logo", "cover", "gallery", "offer"]
          },
          "offer_id": { "type": ["string", "null"] }
        }
      },
      "Service": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "provider_type",
          "display_name",
          "summary",
          "service_mode",
          "category",
          "location",
          "verified",
          "languages",
          "tags",
          "offers",
          "links"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "provider_type": {
            "type": "string",
            "enum": ["person", "organization"]
          },
          "display_name": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "service_mode": {
            "type": "string",
            "enum": ["local", "online", "both"]
          },
          "category": {
            "$ref": "#/components/schemas/Category"
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "geo": {
            "$ref": "#/components/schemas/Geo"
          },
          "service_areas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceArea"
            }
          },
          "contact": {
            "$ref": "#/components/schemas/Contact"
          },
          "verified": {
            "type": "boolean"
          },
          "published_at": {
            "type": "string",
            "format": "date-time"
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Offer"
            }
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Image"
            }
          },
          "sponsored": {
            "type": "boolean",
            "default": false
          },
          "links": {
            "type": "object",
            "additionalProperties": {
              "type": ["string", "null"]
            }
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "required": [
          "data",
          "next_cursor",
          "as_of",
          "facets",
          "applied_filters",
          "links"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Service"
            }
          },
          "next_cursor": {
            "type": ["string", "null"]
          },
          "as_of": {
            "type": "string",
            "format": "date-time"
          },
          "total_estimate": {
            "type": "integer"
          },
          "facets": {
            "type": "object",
            "additionalProperties": true
          },
          "applied_filters": {
            "type": "object",
            "additionalProperties": true
          },
          "links": {
            "type": "object",
            "additionalProperties": {
              "type": ["string", "null"]
            }
          },
          "meta": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "RankingEntry": {
        "type": "object",
        "required": [
          "rank",
          "id",
          "display_name",
          "description",
          "category_slug",
          "category_name",
          "destination_url",
          "display_url",
          "visit_url",
          "click_count",
          "sponsored",
          "effective_score",
          "score_as_of",
          "first_credited_at"
        ],
        "properties": {
          "rank": {
            "type": "integer",
            "minimum": 1
          },
          "id": {
            "type": "string"
          },
          "listing_id": {
            "type": ["string", "null"]
          },
          "display_name": {
            "type": "string"
          },
          "description": {
            "type": ["string", "null"],
            "maxLength": 150,
            "description": "The sponsor-provided description, or null. subtitle may instead fall back to linked listing or location copy."
          },
          "category_slug": {
            "type": "string"
          },
          "category_name": {
            "type": "string"
          },
          "destination_url": {
            "type": ["string", "null"]
          },
          "display_url": {
            "type": ["string", "null"],
            "description": "Compact human-readable domain and path."
          },
          "visit_url": {
            "type": ["string", "null"],
            "description": "Same-origin redirect that records an outbound sponsored click before opening the destination."
          },
          "click_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Aggregate outbound sponsor-link opens recorded by Seekuno."
          },
          "subtitle": {
            "type": ["string", "null"]
          },
          "logo_url": {
            "type": ["string", "null"]
          },
          "sponsored": {
            "const": true
          },
          "effective_score": {
            "type": "number",
            "minimum": 0
          },
          "score_as_of": {
            "type": "string",
            "format": "date-time"
          },
          "first_credited_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RankingResponse": {
        "type": "object",
        "required": ["data", "as_of", "ranking_policy"],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RankingEntry"
            }
          },
          "as_of": {
            "type": "string",
            "format": "date-time"
          },
          "ranking_policy": {
            "type": "object",
            "required": [
              "currency",
              "points_per_usd",
              "decay_percent_per_completed_24_hours",
              "formula"
            ],
            "properties": {
              "currency": {
                "const": "USD"
              },
              "points_per_usd": {
                "const": 1
              },
              "decay_percent_per_completed_24_hours": {
                "const": 1
              },
              "formula": {
                "type": "string"
              }
            }
          },
          "meta": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "PromotionDraftResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "status",
              "display_name",
              "description",
              "category_slug",
              "listing_id",
              "destination_url",
              "checkout_url",
              "links"
            ],
            "properties": {
              "id": { "type": "string" },
              "status": {
                "type": "string",
                "enum": ["draft", "active", "paused", "rejected", "ended"]
              },
              "display_name": { "type": "string" },
              "description": {
                "type": ["string", "null"],
                "maxLength": 150
              },
              "category_slug": { "type": ["string", "null"] },
              "listing_id": { "type": ["string", "null"] },
              "destination_url": {
                "type": ["string", "null"],
                "format": "uri"
              },
              "checkout_url": { "type": "string", "format": "uri-reference" },
              "links": {
                "type": "object",
                "additionalProperties": { "type": "string" }
              }
            }
          },
          "meta": {
            "type": "object",
            "required": ["idempotency_replayed"],
            "properties": { "idempotency_replayed": { "type": "boolean" } }
          }
        }
      },
      "PromotionCheckoutResponse": {
        "type": "object",
        "required": ["data"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "attempt_id",
              "checkout_url",
              "status",
              "amount_cents",
              "currency",
              "starting_score"
            ],
            "properties": {
              "attempt_id": { "type": "string" },
              "checkout_url": { "type": "string", "format": "uri" },
              "status": { "type": "string" },
              "amount_cents": {
                "type": "integer",
                "minimum": 500,
                "description": "Digital sponsored-ranking credit amount in USD cents; tax is not included in the score credit."
              },
              "currency": { "const": "USD" },
              "starting_score": {
                "type": "number",
                "description": "Starting score equal to the sponsored-credit amount in USD; $1 = 1 point."
              },
              "decay": {
                "type": "string",
                "description": "Each independent credit is reduced by 1% after every completed 24-hour period."
              }
            }
          }
        }
      },
      "ListingValidation": {
        "type": "object",
        "required": [
          "valid",
          "normalized",
          "missing_required",
          "errors",
          "warnings"
        ],
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "normalized": {
            "type": "object",
            "additionalProperties": true
          },
          "missing_required": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "JSON Pointer path"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationMessage"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationMessage"
            }
          }
        }
      },
      "ListingDraftResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "slug",
              "status",
              "provider_type",
              "display_name",
              "category",
              "service_mode",
              "service_area_count",
              "offer_count",
              "offers",
              "media_upload_url"
            ],
            "properties": {
              "id": { "type": "string" },
              "slug": { "type": "string" },
              "status": {
                "type": "string",
                "enum": [
                  "draft",
                  "pending",
                  "published",
                  "suspended",
                  "archived"
                ],
                "description": "The listing's current persisted status. An idempotent replay can return a status reached after draft creation."
              },
              "provider_type": {
                "type": "string",
                "enum": ["person", "organization"]
              },
              "display_name": { "type": "string" },
              "category": { "type": "string" },
              "service_mode": {
                "type": "string",
                "enum": ["local", "online", "both"]
              },
              "service_area_count": { "type": "integer", "minimum": 0 },
              "offer_count": { "type": "integer", "minimum": 0 },
              "offers": {
                "type": "array",
                "description": "Persisted offers in display order. Use the returned id as X-Offer-Id when X-Image-Role is offer; never invent an offer ID.",
                "items": {
                  "type": "object",
                  "required": ["id", "name"],
                  "properties": {
                    "id": { "type": "string" },
                    "name": { "type": "string" }
                  }
                }
              },
              "media_upload_url": {
                "type": "string",
                "format": "uri-reference"
              }
            }
          },
          "meta": {
            "type": "object",
            "required": ["idempotency_replayed"],
            "properties": {
              "idempotency_replayed": { "type": "boolean" }
            }
          }
        }
      },
      "MediaUploadResponse": {
        "type": "object",
        "required": ["data"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "listing_id",
              "offer_id",
              "content_type",
              "size_bytes",
              "alt_text",
              "role",
              "status",
              "public_url",
              "public_url_when_ready"
            ],
            "properties": {
              "id": { "type": "string" },
              "listing_id": { "type": "string" },
              "offer_id": { "type": ["string", "null"] },
              "content_type": {
                "type": "string",
                "enum": ["image/jpeg", "image/png", "image/webp"]
              },
              "size_bytes": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10485760
              },
              "alt_text": {
                "type": "string",
                "minLength": 1,
                "maxLength": 300
              },
              "role": {
                "type": "string",
                "enum": ["logo", "cover", "gallery", "offer"]
              },
              "status": { "const": "pending" },
              "public_url": { "type": "null" },
              "public_url_when_ready": {
                "type": "string",
                "format": "uri-reference",
                "description": "This path remains unavailable until trusted moderation marks the media ready and the listing is published."
              }
            }
          }
        }
      },
      "MediaUploadReplayResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "listing_id",
              "offer_id",
              "content_type",
              "size_bytes",
              "alt_text",
              "role",
              "status",
              "public_url",
              "public_url_when_ready"
            ],
            "properties": {
              "id": { "type": "string" },
              "listing_id": { "type": "string" },
              "offer_id": { "type": ["string", "null"] },
              "content_type": {
                "type": "string",
                "enum": ["image/jpeg", "image/png", "image/webp"]
              },
              "size_bytes": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10485760
              },
              "alt_text": {
                "type": "string",
                "minLength": 1,
                "maxLength": 300
              },
              "role": {
                "type": "string",
                "enum": ["logo", "cover", "gallery", "offer"]
              },
              "status": { "enum": ["pending", "ready"] },
              "public_url": {
                "type": ["string", "null"],
                "format": "uri-reference",
                "description": "Present only when media is ready and its listing is published."
              },
              "public_url_when_ready": {
                "type": "string",
                "format": "uri-reference",
                "description": "This path remains unavailable until trusted moderation marks the media ready and the listing is published."
              }
            }
          },
          "meta": {
            "type": "object",
            "required": ["idempotency_replayed"],
            "properties": {
              "idempotency_replayed": { "const": true }
            }
          }
        }
      },
      "OwnedListingOffer": {
        "type": "object",
        "required": ["id", "name", "price_label"],
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "price_label": { "type": ["string", "null"] }
        }
      },
      "OwnedListing": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "provider_type",
          "display_name",
          "summary",
          "status",
          "service_mode",
          "location_label",
          "category",
          "offer_count",
          "offers",
          "media_count",
          "created_at",
          "updated_at",
          "published_at",
          "public_url",
          "publish_url"
        ],
        "properties": {
          "id": { "type": "string" },
          "slug": { "type": "string" },
          "provider_type": {
            "type": "string",
            "enum": ["person", "organization"]
          },
          "display_name": { "type": "string" },
          "summary": { "type": "string" },
          "status": {
            "type": "string",
            "enum": ["draft", "pending", "published", "suspended", "archived"]
          },
          "service_mode": {
            "type": "string",
            "enum": ["local", "online", "both"]
          },
          "location_label": { "type": ["string", "null"] },
          "category": {
            "type": ["object", "null"],
            "required": ["slug", "name"],
            "properties": {
              "slug": { "type": "string" },
              "name": { "type": "string" }
            }
          },
          "offer_count": { "type": "integer", "minimum": 0 },
          "offers": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/OwnedListingOffer" }
          },
          "media_count": { "type": "integer", "minimum": 0 },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" },
          "published_at": {
            "type": ["string", "null"],
            "format": "date-time"
          },
          "public_url": {
            "type": ["string", "null"],
            "format": "uri-reference"
          },
          "publish_url": { "type": "string", "format": "uri-reference" }
        }
      },
      "OwnedListingsResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/OwnedListing" }
          },
          "meta": {
            "type": "object",
            "required": ["returned", "truncated"],
            "properties": {
              "returned": { "type": "integer", "minimum": 0 },
              "truncated": { "type": "boolean" }
            }
          }
        }
      },
      "ListingPublishResponse": {
        "type": "object",
        "required": ["data", "meta"],
        "properties": {
          "data": {
            "type": "object",
            "required": [
              "id",
              "slug",
              "display_name",
              "status",
              "public_url",
              "api_url"
            ],
            "properties": {
              "id": { "type": "string" },
              "slug": { "type": "string" },
              "display_name": { "type": "string" },
              "status": { "const": "published" },
              "public_url": { "type": "string", "format": "uri-reference" },
              "api_url": { "type": "string", "format": "uri-reference" }
            }
          },
          "meta": {
            "type": "object",
            "required": ["already_published"],
            "properties": {
              "already_published": { "type": "boolean" }
            }
          }
        }
      },
      "ValidationMessage": {
        "type": "object",
        "required": ["path", "code", "message"],
        "properties": {
          "path": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Problem": {
        "type": "object",
        "required": ["type", "title", "status", "detail"],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri-reference"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "errors": {}
        }
      }
    }
  }
}
