openapi: 3.1.0
info:
  title: mongrl API
  version: 0.1.0
  description: "The API under your marketplace: accounts, listings, search, selling, checkout, orders, payouts and messages. Guides: https://dev.mongrl.com. Everything for coding agents: https://dev.mongrl.com/llms.txt"
servers:
  - url: https://api.mongrl.com
    description: Production (live keys)
paths:
  /v1/catalog-terms:
    get:
      description: E.g. all brands, ordered by name, a page at a time; `q` filters by the start of the name.
      operationId: listCatalogTerms
      parameters:
        - description: One of the marketplace's term kinds
          explode: false
          in: query
          name: kind
          required: true
          schema:
            description: One of the marketplace's term kinds
            examples:
              - brand
            type: string
        - description: Only names starting with this
          explode: false
          in: query
          name: q
          schema:
            description: Only names starting with this
            maxLength: 200
            type: string
        - explode: false
          in: query
          name: limit
          schema:
            default: 50
            format: int64
            maximum: 200
            minimum: 1
            type: integer
        - description: next_cursor from the previous page
          explode: false
          in: query
          name: cursor
          schema:
            description: next_cursor from the previous page
            type: string
        - description: Language for descriptions
          explode: false
          in: query
          name: locale
          schema:
            description: Language for descriptions
            type: string
        - description: Only terms that listings on sale use, all at once (no paging, q ignored), each with listing_count
          explode: false
          in: query
          name: in_use
          schema:
            description: Only terms that listings on sale use, all at once (no paging, q ignored), each with listing_count
            type: boolean
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicTermPageBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: List catalogue terms of a kind
      tags:
        - catalogue
  /v1/catalog-terms/{kind}/{slug}:
    get:
      description: "E.g. a designer's page: /v1/catalog-terms/designer/poul-henningsen."
      operationId: getCatalogTerm
      parameters:
        - in: path
          name: kind
          required: true
          schema:
            type: string
        - in: path
          name: slug
          required: true
          schema:
            type: string
        - description: Language for the description
          explode: false
          in: query
          name: locale
          schema:
            description: Language for the description
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TermBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Get a catalogue term by its slug
      tags:
        - catalogue
  /v1/categories:
    get:
      description: All categories with parent ids, in one language (falling back to the default).
      operationId: listCategories
      parameters:
        - description: Language for names; the marketplace's default when omitted
          explode: false
          in: query
          name: locale
          schema:
            description: Language for names; the marketplace's default when omitted
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListCategoriesResponse"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: List the category tree
      tags:
        - catalogue
  /v1/email-verifications:
    post:
      operationId: confirmEmail
      parameters:
        - in: header
          name: User-Agent
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TokenInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Confirm an email address with the emailed link
      tags:
        - accounts
  /v1/images/{id}/{file}:
    get:
      description: "Public, for <img> tags: no key needed. `file` is thumb.jpg, medium.jpg or large.jpg. Never changes, so it's cached for a year."
      operationId: getImage
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: path
          name: file
          required: true
          schema:
            enum:
              - thumb.jpg
              - medium.jpg
              - large.jpg
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                contentEncoding: base64
                type: string
          description: OK
          headers:
            Cache-Control:
              schema:
                type: string
            Content-Type:
              schema:
                type: string
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Get a photo
      tags:
        - listings
  /v1/listings:
    get:
      description: "Every parameter is optional. `q` matches the title, catalogue terms (brand, designer…) and description, ignoring accents and matching word beginnings (\"henn\" finds Henningsen). Filters combine: `term` matches any of the terms given per kind (brand A or B, and designer C), `attr` matches any of the values given per attribute. Prices are compared across currencies at the day's rate."
      operationId: listListings
      parameters:
        - explode: false
          in: query
          name: limit
          schema:
            default: 24
            format: int64
            maximum: 100
            minimum: 1
            type: integer
        - description: next_cursor from the previous page
          explode: false
          in: query
          name: cursor
          schema:
            description: next_cursor from the previous page
            type: string
        - description: Words to look for
          explode: false
          in: query
          name: q
          schema:
            description: Words to look for
            maxLength: 200
            type: string
        - description: relevance (default with q), newest (default without), popular (most saved)
          explode: false
          in: query
          name: sort
          schema:
            description: relevance (default with q), newest (default without), popular (most saved)
            enum:
              - relevance
              - newest
              - price_asc
              - price_desc
              - popular
            type: string
        - description: This category and its subcategories
          explode: false
          in: query
          name: category
          schema:
            description: This category and its subcategories
            type: string
        - description: Catalogue term ids, e.g. brands; repeat for several
          explode: true
          in: query
          name: term
          schema:
            description: Catalogue term ids, e.g. brands; repeat for several
            items:
              type: string
            type:
              - array
              - "null"
        - explode: false
          in: query
          name: seller
          schema:
            type: string
        - description: key:value, e.g. condition:excellent; repeat for several
          explode: true
          in: query
          name: attr
          schema:
            description: key:value, e.g. condition:excellent; repeat for several
            items:
              type: string
            type:
              - array
              - "null"
        - description: In minor units of currency
          explode: false
          in: query
          name: min_price
          schema:
            description: In minor units of currency
            format: int64
            minimum: 0
            type: integer
        - description: In minor units of currency
          explode: false
          in: query
          name: max_price
          schema:
            description: In minor units of currency
            format: int64
            minimum: 0
            type: integer
        - description: Show prices in this currency too (display_price); one of the marketplace's
          explode: false
          in: query
          name: currency
          schema:
            description: Show prices in this currency too (display_price); one of the marketplace's
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Search listings on sale
      tags:
        - listings
  /v1/listings/{id}:
    get:
      description: Listings not on sale are 404 here; sellers see theirs under /v1/me/listings.
      operationId: getListing
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - description: Show prices in this currency too (display_price); one of the marketplace's
          explode: false
          in: query
          name: currency
          schema:
            description: Show prices in this currency too (display_price); one of the marketplace's
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Get a listing on sale
      tags:
        - listings
  /v1/locations:
    get:
      description: Active ones, e.g. for a showroom page.
      operationId: listLocations
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: The marketplace's showrooms and warehouses
      tags:
        - marketplace
  /v1/marketplace:
    get:
      description: The marketplace the API key (or console session) belongs to. Handy for checking a key.
      operationId: getMarketplace
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Get the caller's marketplace
      tags:
        - marketplace
  /v1/me:
    get:
      operationId: getMe
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Get the signed-in user
      tags:
        - accounts
    patch:
      description: Only the fields sent change.
      operationId: updateMe
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateMeInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Update the signed-in user's profile
      tags:
        - accounts
  /v1/me/conversations:
    get:
      description: Latest first, with the last message and how many are unread.
      operationId: listMyConversations
      parameters:
        - explode: false
          in: query
          name: limit
          schema:
            default: 50
            format: int64
            maximum: 100
            minimum: 1
            type: integer
        - explode: false
          in: query
          name: offset
          schema:
            format: int64
            minimum: 0
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConversationListOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: The signed-in user's conversations
      tags:
        - messages
    post:
      description: About a listing (a question before buying), an order (its buyer or seller), or support (the marketplace). Continues the existing conversation if there is one.
      operationId: startConversation
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StartConversationRequest"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StartConversationResponse"
          description: Created
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Message the seller, the buyer or the marketplace
      tags:
        - messages
  /v1/me/conversations/{id}/messages:
    get:
      description: Oldest first; marks them read.
      operationId: readMyConversation
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - description: Only messages after this id
          explode: false
          in: query
          name: after
          schema:
            description: Only messages after this id
            format: int64
            minimum: 0
            type: integer
        - explode: false
          in: query
          name: limit
          schema:
            default: 100
            format: int64
            maximum: 200
            minimum: 1
            type: integer
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessagesOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Read a conversation
      tags:
        - messages
    post:
      operationId: sendMessage
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MessageInput"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageBody"
          description: Created
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Reply in a conversation
      tags:
        - messages
  /v1/me/favorites:
    get:
      description: Latest saved first, including listings no longer on sale (check status).
      operationId: listFavorites
      parameters:
        - explode: false
          in: query
          name: limit
          schema:
            default: 24
            format: int64
            maximum: 100
            minimum: 1
            type: integer
        - description: next_cursor from the previous page
          explode: false
          in: query
          name: cursor
          schema:
            description: next_cursor from the previous page
            type: string
        - description: Show prices in this currency too (display_price); one of the marketplace's
          explode: false
          in: query
          name: currency
          schema:
            description: Show prices in this currency too (display_price); one of the marketplace's
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: List saved listings
      tags:
        - favourites
  /v1/me/favorites/{id}:
    put:
      description: Only listings on sale. Saving twice is fine.
      operationId: saveFavorite
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "204":
          description: No Content
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Save a listing
      tags:
        - favourites
    delete:
      operationId: removeFavorite
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "204":
          description: No Content
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Remove a saved listing
      tags:
        - favourites
  /v1/me/listings:
    get:
      description: Every status, latest change first.
      operationId: listMyListings
      parameters:
        - explode: false
          in: query
          name: limit
          schema:
            default: 24
            format: int64
            maximum: 100
            minimum: 1
            type: integer
        - description: next_cursor from the previous page
          explode: false
          in: query
          name: cursor
          schema:
            description: next_cursor from the previous page
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: List the signed-in seller's listings
      tags:
        - selling
    post:
      description: Saved as a draft; it may be incomplete until it's published.
      operationId: createMyListing
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ListingInputBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: Created
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Start a listing
      tags:
        - selling
  /v1/me/listings/{id}:
    get:
      operationId: getMyListing
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Get one of the seller's listings
      tags:
        - selling
    put:
      description: A listing on sale must stay complete. Closed listings can't be edited.
      operationId: updateMyListing
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ListingInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Replace a listing's content
      tags:
        - selling
    delete:
      description: Listings that were ever on sale are closed instead.
      operationId: deleteMyListing
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "204":
          description: No Content
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Delete a draft
      tags:
        - selling
  /v1/me/listings/{id}/close:
    post:
      operationId: closeMyListing
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Take a listing off sale
      tags:
        - selling
  /v1/me/listings/{id}/images:
    post:
      description: Send the photo as the body (JPEG, PNG or WebP, up to 20 MB and 50 megapixels). It's "processing" until its sizes are made, usually within seconds. At most 20 per listing.
      operationId: uploadListingImage
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          image/jpeg:
            schema:
              contentMediaType: application/octet-stream
              format: binary
              type: string
        required: true
      responses:
        "202":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ImageBody"
          description: Accepted
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Add a photo
      tags:
        - selling
  /v1/me/listings/{id}/images/order:
    put:
      description: Send every image id once; the first becomes the main photo.
      operationId: reorderListingImages
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReorderListingImagesRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Set the order of the photos
      tags:
        - selling
  /v1/me/listings/{id}/images/{image_id}:
    delete:
      operationId: deleteListingImage
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: path
          name: image_id
          required: true
          schema:
            type: string
      responses:
        "204":
          description: No Content
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Remove a photo
      tags:
        - selling
  /v1/me/listings/{id}/offer/accept:
    post:
      description: The offer becomes the price and the listing waits for approval.
      operationId: acceptMyListingOffer
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Accept the marketplace's price
      tags:
        - selling
  /v1/me/listings/{id}/offer/counter:
    post:
      description: The seller's price is set and the listing waits for approval; the team approves it, denies it or makes a new offer.
      operationId: counterMyListingOffer
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CounterMyListingOfferRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Answer the marketplace's price with your own
      tags:
        - selling
  /v1/me/listings/{id}/publish:
    post:
      description: "From draft or closed. Goes to approval first if the marketplace requires it, with only the basics checked (title, photos, country, price), since the team completes the listing. With ask_valuation (where the marketplace offers valuations) no price is needed: the team offers one, which the seller accepts or answers with their own. If anything is missing, the 422 lists every problem."
      operationId: publishMyListing
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PublishMyListingRequest"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Put a listing on sale
      tags:
        - selling
  /v1/me/orders:
    get:
      description: As buyer (purchases) or seller (sales), newest first.
      operationId: listMyOrders
      parameters:
        - explode: false
          in: query
          name: state
          schema:
            type: string
        - explode: false
          in: query
          name: limit
          schema:
            default: 25
            format: int64
            maximum: 100
            minimum: 1
            type: integer
        - explode: false
          in: query
          name: offset
          schema:
            format: int64
            maximum: 10000
            minimum: 0
            type: integer
        - explode: false
          in: query
          name: as
          schema:
            default: buyer
            enum:
              - buyer
              - seller
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderListOutputBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: List the signed-in user's orders
      tags:
        - orders
    post:
      description: Reserves the stock and starts the payment. Pay within 15 minutes with payment.client_secret, or the order expires and the item is released.
      operationId: placeOrder
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PlaceOrderRequest"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PlaceOrderResponse"
          description: Created
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Buy a listing
      tags:
        - orders
  /v1/me/orders/quote:
    post:
      description: What the buyer would pay, line by line, e.g. to show a voucher's effect at checkout. Placing the order prices it the same way.
      operationId: quoteOrder
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QuoteRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QuoteBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Price an order without placing it
      tags:
        - orders
  /v1/me/orders/{id}:
    get:
      description: Its buyer or its seller.
      operationId: getMyOrder
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Get one of the user's orders
      tags:
        - orders
  /v1/me/orders/{id}/delivery-booking:
    get:
      description: The buyer's booked delivery time and the open slots. 409 when the delivery isn't booked by the buyer (not paid yet, collected, or arranged by the carrier).
      operationId: getMyDeliveryBooking
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeliveryBookingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: When the delivery comes
      tags:
        - orders
    put:
      description: One of the open slots; it can be changed until the lead time.
      operationId: bookMyDelivery
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SlotInputBody"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeliveryBookingBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Choose when the delivery comes
      tags:
        - orders
  /v1/me/orders/{id}/transitions/{transition}:
    post:
      description: One of the order's available_transitions for the user, e.g. mark-delivered (seller, with data.tracking_number) or mark-received (buyer).
      operationId: takeMyOrderTransition
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: path
          name: transition
          required: true
          schema:
            examples:
              - mark-delivered
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransitionInputBody"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Move an order on
      tags:
        - orders
  /v1/me/payouts:
    get:
      operationId: getMyPayouts
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PayoutSetupBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: How far the seller's payout setup has come
      tags:
        - payouts
  /v1/me/payouts/session:
    post:
      description: "Returns a client secret for Stripe's embedded onboarding component (account-onboarding). Configure it with collectionOptions {fields: 'currently_due', futureRequirements: 'omit'} to ask only for what's required now. Ask the seller first whether they sell as a private person or a company; the form then asks only that kind's questions."
      operationId: startMyPayoutSetup
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StartMyPayoutSetupRequest"
        required: true
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StartMyPayoutSetupResponse"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Start or continue the seller's payout setup
      tags:
        - payouts
  /v1/me/verification-email:
    post:
      description: Links to `<storefront>/<language>/auth/verify-email?token=…`. Does nothing if the address is already confirmed.
      operationId: resendVerificationEmail
      responses:
        "202":
          description: Accepted
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Send the email confirmation link again
      tags:
        - accounts
  /v1/media/{id}/{file}:
    get:
      description: "Public, for <img> tags: no key needed. `file` is thumb.jpg, medium.jpg, large.jpg or xlarge.jpg. Never changes, so it's cached for a year."
      operationId: getMedia
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: path
          name: file
          required: true
          schema:
            enum:
              - thumb.jpg
              - medium.jpg
              - large.jpg
              - xlarge.jpg
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                contentEncoding: base64
                type: string
          description: OK
          headers:
            Cache-Control:
              schema:
                type: string
            Content-Type:
              schema:
                type: string
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      summary: Get a page image
      tags:
        - pages
  /v1/newsletter/subscribers:
    post:
      description: Always answers 202 for a valid address, so it never reveals who's signed up; signing up again changes nothing. mongrl keeps the list and adds new addresses to the marketplace's Resend contacts. Call from the shop's server.
      operationId: subscribeNewsletter
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SubscribeNewsletterRequest"
        required: true
      responses:
        "202":
          description: Accepted
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Sign up for the newsletter
      tags:
        - newsletter
  /v1/pages:
    get:
      description: Their addresses and languages, e.g. for a sitemap.
      operationId: listPages
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListPagesResponse"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: List the published pages
      tags:
        - pages
  /v1/pages/{slug}:
    get:
      description: "The page at `slug` (`home` is the front page) in `locale`, falling back to the marketplace's default language, with its blocks filled in: listings (on sale, prices in `currency`), images in sizes, categories, the showroom. Blocks with nothing to show are left out. 404 until the page is published."
      operationId: getPage
      parameters:
        - in: path
          name: slug
          required: true
          schema:
            examples:
              - home
            maxLength: 80
            type: string
        - explode: false
          in: query
          name: locale
          schema:
            maxLength: 35
            type: string
        - description: Show listing prices in this currency too (display_price); one of the marketplace's
          explode: false
          in: query
          name: currency
          schema:
            description: Show listing prices in this currency too (display_price); one of the marketplace's
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Get a published page
      tags:
        - pages
  /v1/pages/{slug}/preview:
    get:
      description: "The draft, filled in like a published page, for the console's Preview link: the shop opens `/<slug>?preview=<token>` and passes the token here. Don't cache it, and don't let search engines index it."
      operationId: previewPage
      parameters:
        - in: path
          name: slug
          required: true
          schema:
            examples:
              - home
            maxLength: 80
            type: string
        - explode: false
          in: query
          name: token
          required: true
          schema:
            maxLength: 100
            type: string
        - explode: false
          in: query
          name: currency
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PageBody"
          description: OK
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Preview a page's draft
      tags:
        - pages
  /v1/password-resets:
    post:
      description: Sends a link to `<storefront>/<language>/auth/reset-password?token=…` if the address has an account. Always answers 202.
      operationId: requestPasswordReset
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailInputBody"
        required: true
      responses:
        "202":
          description: Accepted
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Email a password reset link
      tags:
        - accounts
  /v1/password-resets/confirm:
    post:
      description: Signs the user out everywhere; sign them in again with the new password.
      operationId: resetPassword
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ResetInputBody"
        required: true
      responses:
        "204":
          description: No Content
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Set a new password with the emailed link
      tags:
        - accounts
  /v1/sessions:
    post:
      description: After 10 wrong passwords in 15 minutes the account is locked for the rest of that time (429 with Retry-After).
      operationId: signIn
      parameters:
        - in: header
          name: User-Agent
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignInInputBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignedInOutputBody"
          description: Created
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Sign in with email and password
      tags:
        - accounts
  /v1/sessions/current:
    delete:
      description: Ends the session sent as X-User-Session.
      operationId: signOut
      responses:
        "204":
          description: No Content
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
          userSession: []
      summary: Sign out
      tags:
        - accounts
  /v1/sessions/email-link:
    post:
      description: Sends a link to `<storefront>/<language>/auth/sign-in?token=…` if the address has an account. Always answers 202, so it doesn't reveal who has one. At most 5 emails an hour per address.
      operationId: requestSignInLink
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmailInputBody"
        required: true
      responses:
        "202":
          description: Accepted
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Email a sign-in link
      tags:
        - accounts
  /v1/sessions/from-link:
    post:
      description: The token from the link works once. Following the link also confirms the email address.
      operationId: signInWithLink
      parameters:
        - in: header
          name: User-Agent
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TokenInputBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignedInOutputBody"
          description: Created
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Sign in with an emailed link
      tags:
        - accounts
  /v1/users:
    post:
      description: Creates the user and signs them in. Call from the shop's server.
      operationId: signUp
      parameters:
        - in: header
          name: User-Agent
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SignUpInputBody"
        required: true
      responses:
        "201":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignedInOutputBody"
          description: Created
        default:
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/ErrorModel"
          description: Error
      security:
        - apiKey: []
      summary: Create an account
      tags:
        - accounts
components:
  schemas:
    Address:
      additionalProperties: false
      properties:
        city:
          type: string
        country:
          description: ISO 3166, e.g. SE
          type: string
        line1:
          type: string
        line2:
          type: string
        name:
          type: string
        phone:
          type: string
        postal_code:
          type: string
      required:
        - postal_code
        - country
      type: object
    Block:
      additionalProperties: false
      properties:
        body:
          description: Markdown (text)
          type: string
        button_label:
          description: newsletter
          type: string
        buttons:
          description: Up to 2 (hero, cta)
          items:
            $ref: "#/components/schemas/Link"
          type:
            - array
            - "null"
        eyebrow:
          description: A short line above the heading (hero, image_text)
          type: string
        heading:
          description: hero, listing_grid, banner, category_tiles, image_text, featured_listing, cta, newsletter, showroom, faq
          type: string
        id:
          description: Stable id within the page (1-40 letters, digits, - or _)
          examples:
            - hero-1
          type: string
        image:
          $ref: "#/components/schemas/ImageRef"
          description: The image, in sizes
          readOnly: true
        image_id:
          description: "An uploaded image (hero with visual image, banner, image_text, showroom; cta: optional, behind the text)"
          type: string
        image_side:
          description: image_text
          enum:
            - left
            - right
          type: string
        items:
          description: "promises: 1 to 6"
          items:
            $ref: "#/components/schemas/Item"
          type:
            - array
            - "null"
        limit:
          description: "listing_grid: how many to show (1-24, default 8)"
          format: int64
          type: integer
        link:
          $ref: "#/components/schemas/Link"
          description: Where the block leads (listing_grid "view all", banner, image_text)
        links:
          description: 1 to 8 (links)
          items:
            $ref: "#/components/schemas/Link"
          type:
            - array
            - "null"
        listing:
          $ref: "#/components/schemas/ListingBody"
          description: hero (visual listing or newest), featured_listing
          readOnly: true
        listing_id:
          description: hero (visual listing), featured_listing
          type: string
        listing_ids:
          description: listing_grid with source picked, in order
          items:
            type: string
          type:
            - array
            - "null"
        listings:
          description: "listing_grid: the listings on sale to show, in order"
          items:
            $ref: "#/components/schemas/ListingBody"
          readOnly: true
          type:
            - array
            - "null"
        location:
          $ref: "#/components/schemas/LocationRef"
          description: showroom
          readOnly: true
        location_id:
          description: "showroom: one of the marketplace's locations"
          type: string
        phone_image:
          $ref: "#/components/schemas/ImageRef"
          readOnly: true
        phone_image_id:
          description: "banner: the image for phones (the main one otherwise)"
          type: string
        questions:
          description: "faq: 1 to 40"
          items:
            $ref: "#/components/schemas/Question"
          type:
            - array
            - "null"
        rule:
          $ref: "#/components/schemas/Rule"
          description: listing_grid with source rule
        search:
          description: "hero: show a search box"
          type: boolean
        source:
          description: "listing_grid: hand-picked listings or a rule"
          enum:
            - picked
            - rule
          type: string
        text:
          description: Plain text (hero, banner, image_text, featured_listing note, cta, newsletter, showroom)
          type: string
        tiles:
          description: "category_tiles: 1 to 12"
          items:
            $ref: "#/components/schemas/Tile"
          type:
            - array
            - "null"
        tone:
          description: "cta: the band's colour"
          enum:
            - brand
            - light
            - dark
          type: string
        type:
          enum:
            - hero
            - links
            - listing_grid
            - banner
            - category_tiles
            - image_text
            - featured_listing
            - cta
            - newsletter
            - promises
            - showroom
            - text
            - faq
          type: string
        visual:
          description: "hero: what's beside the text; newest is the newest listing on sale"
          enum:
            - image
            - listing
            - newest
            - none
          type: string
        when_sold:
          description: "featured_listing: hide the block once it sells, or keep showing it (as sold)"
          enum:
            - hide
            - show
          type: string
      required:
        - id
        - type
      type: object
    BookingBody:
      additionalProperties: false
      properties:
        horizon_days:
          description: How far ahead buyers can book
          format: int64
          maximum: 120
          minimum: 1
          type: integer
        lead_days:
          description: The earliest slot is this many days after today
          format: int64
          maximum: 30
          minimum: 0
          type: integer
        per_window:
          description: Deliveries per window; 0 is no limit
          format: int64
          minimum: 0
          type: integer
        time_zone:
          description: e.g. Europe/Stockholm (the default)
          type: string
        weekdays:
          description: 1 is Monday, 7 Sunday
          items:
            format: int64
            type: integer
          type:
            - array
            - "null"
        windows:
          items:
            $ref: "#/components/schemas/BookingWindowBody"
          type:
            - array
            - "null"
        zones:
          description: Zone keys whose buyers book
          items:
            type: string
          type:
            - array
            - "null"
      required:
        - weekdays
        - windows
        - lead_days
        - horizon_days
        - zones
      type: object
    BookingWindowBody:
      additionalProperties: false
      properties:
        key:
          type: string
        label:
          description: e.g. 08–12
          type: string
      required:
        - key
        - label
      type: object
    CategoryBody:
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        parent_id:
          type: string
        position:
          format: int64
          type: integer
        slug:
          type: string
      required:
        - id
        - slug
        - name
        - position
      type: object
    CategoryRef:
      additionalProperties: false
      properties:
        id:
          type: string
        name:
          type: string
        slug:
          type: string
      required:
        - id
        - slug
        - name
      type: object
    ChargebackBody:
      additionalProperties: false
      properties:
        id:
          type: string
        reason:
          type: string
        status:
          description: Stripe's dispute status, e.g. needs_response, under_review, won, lost
          type: string
      required:
        - id
        - status
        - reason
      type: object
    ConversationBody:
      additionalProperties: false
      properties:
        id:
          type: string
        kind:
          enum:
            - listing
            - order
            - support
          type: string
        last_message:
          type: string
        last_message_at:
          format: date-time
          type: string
        listing_id:
          type: string
        order_id:
          type: string
        unread:
          format: int64
          type: integer
        user_a:
          description: The buyer, or the user asking for support
          type: string
        user_b:
          description: The seller (not in support conversations)
          type: string
      required:
        - id
        - kind
        - user_a
        - last_message_at
        - unread
      type: object
    ConversationListOutputBody:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/ConversationBody"
          type:
            - array
            - "null"
      required:
        - items
      type: object
    CounterMyListingOfferRequest:
      additionalProperties: false
      properties:
        amount:
          description: In minor units, in the offer's currency
          format: int64
          minimum: 1
          type: integer
      required:
        - amount
      type: object
    Delivery:
      additionalProperties: false
      properties:
        buyer_arranged:
          type: boolean
        pickup:
          type: boolean
        shipping:
          $ref: "#/components/schemas/Shipping"
      type: object
    DeliveryBody:
      additionalProperties: false
      properties:
        address:
          $ref: "#/components/schemas/Address"
          description: Where to ship to (shipping only)
        method:
          description: "One of the listing's; may be omitted when it offers only one. collect: from the marketplace location it's stored at"
          enum:
            - shipping
            - pickup
            - buyer_arranged
            - collect
          type: string
        option:
          description: "Shipping with the marketplace: one of its delivery options (the first when omitted)"
          type: string
      type: object
    DeliveryBookingBody:
      additionalProperties: false
      properties:
        booked:
          $ref: "#/components/schemas/SlotBody"
        changeable:
          description: The booked time can still be changed
          type: boolean
        slots:
          items:
            $ref: "#/components/schemas/SlotBody"
          type:
            - array
            - "null"
      required:
        - changeable
        - slots
      type: object
    EmailInputBody:
      additionalProperties: false
      properties:
        email:
          maxLength: 254
          type: string
      required:
        - email
      type: object
    ErrorModel:
      additionalProperties: false
      properties:
        detail:
          description: A human-readable explanation specific to this occurrence of the problem.
          examples:
            - Property foo is required but is missing.
          type: string
        errors:
          description: Optional list of individual error details
          items:
            $ref: "#/components/schemas/ErrorDetail"
          type:
            - array
            - "null"
        instance:
          description: A URI reference that identifies the specific occurrence of the problem.
          examples:
            - https://example.com/error-log/abc123
          format: uri
          type: string
        status:
          description: HTTP status code
          examples:
            - 400
          format: int64
          type: integer
        title:
          description: A short, human-readable summary of the problem type. This value should not change between occurrences of the error.
          examples:
            - Bad Request
          type: string
        type:
          default: about:blank
          description: A URI reference to human-readable documentation for the error.
          examples:
            - https://example.com/errors/example
          format: uri
          type: string
      type: object
    EventBody:
      additionalProperties: false
      properties:
        actor:
          enum:
            - buyer
            - seller
            - operator
            - system
          type: string
        at:
          format: date-time
          type: string
        data:
          additionalProperties:
            type: string
          type: object
        from:
          type: string
        to:
          type: string
        transition:
          type: string
      required:
        - transition
        - from
        - to
        - actor
        - at
      type: object
    ImageBody:
      additionalProperties: false
      properties:
        height:
          format: int64
          type: integer
        id:
          type: string
        status:
          description: Sizes exist once ready
          enum:
            - processing
            - ready
            - failed
          type: string
        urls:
          additionalProperties:
            type: string
          description: thumb (400 px square), medium (800 px), large (1600 px); cacheable forever
          type: object
        width:
          format: int64
          type: integer
      required:
        - id
        - status
      type: object
    ImageRef:
      additionalProperties: false
      properties:
        alt:
          type: string
        height:
          format: int64
          type: integer
        id:
          type: string
        urls:
          additionalProperties:
            type: string
          description: thumb (400 px square), medium (800 px), large (1600 px), xlarge (2400 px); cacheable forever
          type: object
        width:
          format: int64
          type: integer
      required:
        - id
        - alt
        - width
        - height
        - urls
      type: object
    Item:
      additionalProperties: false
      properties:
        text:
          type: string
        title:
          type: string
      required:
        - title
      type: object
    LineItemBody:
      additionalProperties: false
      properties:
        amount:
          format: int64
          type: integer
        code:
          type: string
        label:
          type: string
        quantity:
          format: int64
          type: integer
        unit_price:
          format: int64
          type: integer
      required:
        - code
        - label
        - unit_price
        - quantity
        - amount
      type: object
    Link:
      additionalProperties: false
      properties:
        href:
          description: A path in the shop (/shop?sort=newest) or an https:// address
          maxLength: 500
          type: string
        label:
          maxLength: 60
          type: string
        style:
          description: "Buttons: the first is usually primary"
          enum:
            - primary
            - secondary
          type: string
      required:
        - label
        - href
      type: object
    ListCategoriesResponse:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/CategoryBody"
          type:
            - array
            - "null"
      required:
        - items
      type: object
    ListOutputBody:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/SiteBody"
          type:
            - array
            - "null"
      required:
        - items
      type: object
    ListPagesResponse:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/PublishedPageBody"
          type:
            - array
            - "null"
      required:
        - items
      type: object
    ListingBody:
      additionalProperties: false
      properties:
        attributes:
          additionalProperties: {}
          description: The marketplace's own fields
          type: object
        category_id:
          type: string
        closed_at:
          format: date-time
          type: string
        commission_percent:
          description: "Console only: overrides the marketplace's commission; absent means the default"
          format: double
          type: number
        created_at:
          format: date-time
          type: string
        custody:
          description: "Where it is: at the seller's, being collected, or stored"
          enum:
            - seller
            - collecting
            - stored
          type: string
        delivery:
          $ref: "#/components/schemas/Delivery"
          description: How the item can reach the buyer
        description:
          type: string
        display_price:
          $ref: "#/components/schemas/MoneyBody"
          description: The price in the requested currency, converted at the day's rate (approximate; the final amount is set at checkout)
        favorite_count:
          description: How many users have saved it
          format: int64
          type: integer
        favorited:
          description: Whether the signed-in user has saved it (only with X-User-Session)
          type: boolean
        fulfilment:
          description: Who moves it to the buyer
          enum:
            - seller
            - marketplace
          type: string
        id:
          type: string
        images:
          description: In display order; the first is the main photo
          items:
            $ref: "#/components/schemas/ImageBody"
          type:
            - array
            - "null"
        locale:
          description: The language the seller wrote in
          type: string
        location:
          $ref: "#/components/schemas/LocationBody"
        offer:
          $ref: "#/components/schemas/MoneyBody"
          description: The price the marketplace offers (valuation_offered)
        offer_note:
          description: A note with the offer
          type: string
        offer_schedule:
          description: The price drops offered with the price (valuation_offered)
          items:
            $ref: "#/components/schemas/PriceStepBody"
          type:
            - array
            - "null"
        price:
          $ref: "#/components/schemas/MoneyBody"
          description: Base price, in the seller's currency
        price_schedule:
          description: The price falls in these steps while on sale
          items:
            $ref: "#/components/schemas/PriceStepBody"
          type:
            - array
            - "null"
        published_at:
          format: date-time
          type: string
        review_note:
          description: Why the marketplace denied it (denied)
          type: string
        seller_blocked:
          description: The seller is blocked, so the listing is out of the shop
          type: boolean
        seller_id:
          type: string
        shipping_size:
          description: Prices delivery from the marketplace's shipping table
          type: string
        site:
          $ref: "#/components/schemas/SiteRefBody"
          description: The marketplace location it's stored at or being collected to
        status:
          enum:
            - draft
            - pending_valuation
            - valuation_offered
            - pending_approval
            - published
            - closed
            - denied
          type: string
        stock:
          format: int64
          type: integer
        stored_at:
          description: Stored since
          format: date-time
          type: string
        terms:
          additionalProperties:
            $ref: "#/components/schemas/TermRefBody"
          description: By kind, e.g. brand
          type: object
        title:
          type: string
        updated_at:
          format: date-time
          type: string
        wants_storage:
          description: The seller asked to have it collected and stored
          type: boolean
      required:
        - id
        - seller_id
        - status
        - title
        - description
        - locale
        - stock
        - terms
        - attributes
        - location
        - delivery
        - fulfilment
        - custody
        - images
        - favorite_count
        - created_at
        - updated_at
      type: object
    ListingInputBody:
      additionalProperties: false
      properties:
        attributes:
          additionalProperties: {}
          type: object
        category_id:
          type: string
        delivery:
          $ref: "#/components/schemas/Delivery"
          description: At least one method is needed to publish
        description:
          maxLength: 10000
          type: string
        locale:
          description: The marketplace's default when omitted
          type: string
        location:
          $ref: "#/components/schemas/LocationBody"
        price:
          $ref: "#/components/schemas/MoneyBody"
        shipping_size:
          description: "Operators: one of the marketplace's shipping sizes, which prices delivery; \"\" for none, omitted to leave it"
          type: string
        stock:
          description: 1 when omitted
          format: int64
          type: integer
        terms:
          additionalProperties:
            type: string
          description: 'Catalogue term ids by kind, e.g. {"brand": "…"}'
          type: object
        title:
          maxLength: 140
          type: string
      type: object
    ListingRules:
      additionalProperties: false
      properties:
        attributes_schema:
          description: JSON Schema for the marketplace's own listing fields
        min_images:
          description: Photos needed to go on sale (to be valued, at least 1)
          format: int64
          type: integer
        require_approval:
          description: Listings are reviewed before they go on sale
          type: boolean
        require_category:
          type: boolean
        required_terms:
          description: Term kinds a listing needs, e.g. brand
          items:
            type: string
          type:
            - array
            - "null"
        valuations:
          description: Sellers may ask the marketplace to value a listing instead of setting a price
          type: boolean
      required:
        - valuations
        - require_approval
        - require_category
        - required_terms
        - min_images
      type: object
    LocationBody:
      additionalProperties: false
      properties:
        city:
          type: string
        country:
          description: ISO 3166-1 alpha-2, e.g. SE
          type: string
        postal_code:
          type: string
      type: object
    LocationRef:
      additionalProperties: false
      properties:
        buyer_pickup:
          type: boolean
        city:
          type: string
        country:
          type: string
        kind:
          type: string
        line1:
          type: string
        name:
          type: string
        opening_hours:
          type: string
        postal_code:
          type: string
      required:
        - name
        - kind
        - line1
        - postal_code
        - city
        - country
        - opening_hours
        - buyer_pickup
      type: object
    MessageBody:
      additionalProperties: false
      properties:
        at:
          format: date-time
          type: string
        body:
          type: string
        from_marketplace:
          description: Written by the marketplace (support)
          type: boolean
        id:
          format: int64
          type: integer
        sender_id:
          description: The user who wrote it
          type: string
      required:
        - id
        - from_marketplace
        - body
        - at
      type: object
    MessageInput:
      additionalProperties: false
      properties:
        body:
          maxLength: 5000
          minLength: 1
          type: string
      required:
        - body
      type: object
    MessagesOutputBody:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/MessageBody"
          type:
            - array
            - "null"
      required:
        - items
      type: object
    MoneyBody:
      additionalProperties: false
      properties:
        amount:
          description: "In minor units: 125000 SEK is 1 250,00 kr"
          format: int64
          minimum: 0
          type: integer
        currency:
          description: ISO 4217, one of the marketplace's
          type: string
      required:
        - amount
        - currency
      type: object
    OrderBody:
      additionalProperties: false
      properties:
        available_transitions:
          description: What the caller can do now, e.g. mark-delivered
          items:
            type: string
          type:
            - array
            - "null"
        buyer_id:
          type: string
        chargeback:
          $ref: "#/components/schemas/ChargebackBody"
        created_at:
          format: date-time
          type: string
        currency:
          type: string
        delivery:
          additionalProperties:
            type: string
          description: Tracking number, carrier… as given when marked delivered
          type: object
        history:
          items:
            $ref: "#/components/schemas/EventBody"
          type:
            - array
            - "null"
        id:
          type: string
        line_items:
          items:
            $ref: "#/components/schemas/LineItemBody"
          type:
            - array
            - "null"
        listing_id:
          type: string
        payout:
          $ref: "#/components/schemas/PayoutBody"
        quantity:
          format: int64
          type: integer
        refunded_outside:
          description: Refunded in Stripe outside the order, in the order's currency
          format: int64
          type: integer
        seller_id:
          type: string
        state:
          description: pending_payment, purchased, delivered, received, disputed, canceled, payment_expired
          type: string
        state_entered_at:
          format: date-time
          type: string
        total:
          description: In minor units of currency
          format: int64
          type: integer
      required:
        - id
        - state
        - listing_id
        - buyer_id
        - seller_id
        - quantity
        - currency
        - total
        - line_items
        - delivery
        - history
        - available_transitions
        - state_entered_at
        - created_at
      type: object
    OrderListOutputBody:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/OrderBody"
          type:
            - array
            - "null"
      required:
        - items
      type: object
    PageBody:
      additionalProperties: false
      properties:
        blocks:
          description: In order, filled in with live data; render each by its type and skip types you don't know
          items:
            $ref: "#/components/schemas/Block"
          type:
            - array
            - "null"
        description:
          description: For search results and link previews
          type: string
        locale:
          type: string
        published_at:
          format: date-time
          type: string
        share_image:
          $ref: "#/components/schemas/ImageRef"
          description: For link previews (og:image)
        slug:
          description: home is the front page
          type: string
        title:
          description: For the browser tab and search results
          type: string
      required:
        - slug
        - locale
        - title
        - description
        - blocks
      type: object
    PageOutputBody:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/ListingBody"
          type:
            - array
            - "null"
        next_cursor:
          type: string
      required:
        - items
      type: object
    PaymentStruct:
      additionalProperties: false
      properties:
        client_secret:
          description: For the payment form
          type: string
      required:
        - client_secret
      type: object
    PayoutBody:
      additionalProperties: false
      properties:
        amount:
          description: What the seller is paid
          format: int64
          type: integer
        commission:
          format: int64
          type: integer
        currency:
          type: string
        status:
          description: pending until the buyer has the item
          enum:
            - pending
            - paid
            - waiting_for_payout_details
            - held_for_chargeback
          type: string
        storage_fee:
          description: Taken for storage, per started month
          format: int64
          type: integer
        storage_months:
          format: int64
          type: integer
        subtotal:
          format: int64
          type: integer
      required:
        - currency
        - subtotal
        - commission
        - amount
        - status
      type: object
    PayoutSetupBody:
      additionalProperties: false
      properties:
        due:
          description: What Stripe still needs, when action is required
          items:
            type: string
          type:
            - array
            - "null"
        status:
          enum:
            - none
            - action_required
            - pending
            - ready
          type: string
      required:
        - status
      type: object
    PlaceOrderRequest:
      additionalProperties: false
      properties:
        currency:
          description: What the buyer pays in, one of the marketplace's; the listing's when omitted
          type: string
        delivery:
          $ref: "#/components/schemas/DeliveryBody"
        listing_id:
          type: string
        quantity:
          description: 1 when omitted
          format: int64
          maximum: 100
          minimum: 1
          type: integer
        voucher_code:
          maxLength: 40
          type: string
      required:
        - listing_id
      type: object
    PlaceOrderResponse:
      additionalProperties: false
      properties:
        order:
          $ref: "#/components/schemas/OrderBody"
        payment:
          $ref: "#/components/schemas/PaymentStruct"
      required:
        - order
        - payment
      type: object
    PriceStepBody:
      additionalProperties: false
      properties:
        amount:
          description: The new price, in minor units
          format: int64
          minimum: 1
          type: integer
        done:
          description: It has applied
          readOnly: true
          type: boolean
        months:
          description: Months after the schedule starts (the listing goes on sale, or the schedule changes while on sale)
          format: int64
          maximum: 60
          minimum: 1
          type: integer
        on:
          description: When it applies (once on sale)
          format: date-time
          readOnly: true
          type: string
      required:
        - months
        - amount
      type: object
    PublicBody:
      additionalProperties: false
      properties:
        currencies:
          description: ISO 4217 codes; the first is the default. Empty until chosen.
          items:
            type: string
          type:
            - array
            - "null"
        id:
          type: string
        listings:
          $ref: "#/components/schemas/ListingRules"
          description: What sellers need to know to list
        locales:
          description: BCP 47 language tags; the first is the default.
          items:
            type: string
          type:
            - array
            - "null"
        name:
          type: string
        shipping:
          $ref: "#/components/schemas/ShippingBody"
          description: "Delivery prices by size and zone (empty: listings have their own)"
        slug:
          type: string
        term_kinds:
          description: Kinds of catalogue terms listings refer to, e.g. brand
          items:
            $ref: "#/components/schemas/TermKindBody"
          type:
            - array
            - "null"
      required:
        - id
        - slug
        - name
        - currencies
        - locales
        - term_kinds
        - listings
        - shipping
      type: object
    PublicTermPageBody:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/TermBody"
          type:
            - array
            - "null"
        next_cursor:
          type: string
      required:
        - items
      type: object
    PublishMyListingRequest:
      additionalProperties: false
      properties:
        ask_valuation:
          description: Ask the marketplace to value it instead of setting a price
          type: boolean
        wants_storage:
          description: Ask the marketplace to collect and store it until it sells (otherwise it stays with the seller)
          type: boolean
      type: object
    PublishedPageBody:
      additionalProperties: false
      properties:
        locale:
          type: string
        published_at:
          format: date-time
          type: string
        slug:
          type: string
      required:
        - slug
        - locale
        - published_at
      type: object
    Question:
      additionalProperties: false
      properties:
        answer:
          type: string
        question:
          type: string
      required:
        - question
        - answer
      type: object
    QuoteBody:
      additionalProperties: false
      properties:
        currency:
          type: string
        line_items:
          items:
            $ref: "#/components/schemas/LineItemBody"
          type:
            - array
            - "null"
        total:
          format: int64
          type: integer
      required:
        - currency
        - total
        - line_items
      type: object
    QuoteRequest:
      additionalProperties: false
      properties:
        currency:
          description: What the buyer pays in; the listing's when omitted
          type: string
        delivery:
          $ref: "#/components/schemas/DeliveryBody"
        listing_id:
          type: string
        quantity:
          description: 1 when omitted
          format: int64
          maximum: 100
          minimum: 1
          type: integer
        voucher_code:
          maxLength: 40
          type: string
      required:
        - listing_id
      type: object
    ReorderListingImagesRequest:
      additionalProperties: false
      properties:
        image_ids:
          items:
            type: string
          type:
            - array
            - "null"
      required:
        - image_ids
      type: object
    ResetInputBody:
      additionalProperties: false
      properties:
        password:
          maxLength: 128
          type: string
        token:
          description: From the emailed link's ?token=
          maxLength: 200
          type: string
      required:
        - token
        - password
      type: object
    Rule:
      additionalProperties: false
      properties:
        category_id:
          type: string
        sort:
          enum:
            - newest
            - price_asc
            - price_desc
            - popular
          type: string
        term_ids:
          items:
            type: string
          type:
            - array
            - "null"
      type: object
    SearchOutputBody:
      additionalProperties: false
      properties:
        items:
          items:
            $ref: "#/components/schemas/ListingBody"
          type:
            - array
            - "null"
        next_cursor:
          type: string
        total:
          description: How many listings match
          format: int64
          type: integer
      required:
        - items
        - total
      type: object
    SessionBody:
      additionalProperties: false
      properties:
        expires_at:
          description: Extended while the session is used
          format: date-time
          type: string
        token:
          description: Send as the X-User-Session header. Shown only here.
          type: string
      required:
        - token
        - expires_at
      type: object
    Shipping:
      additionalProperties: false
      properties:
        additional_item_price:
          format: int64
          minimum: 0
          type: integer
        price:
          description: For the first item; omit for "ask for a quote"
          format: int64
          minimum: 0
          type: integer
      type: object
    ShippingBody:
      additionalProperties: false
      properties:
        booking:
          $ref: "#/components/schemas/BookingBody"
          description: When buyers can choose their delivery time
        collection:
          additionalProperties:
            additionalProperties:
              format: int64
              type: integer
            type: object
          description: Collecting from the seller, by size and the seller's zone; missing means quoted
          type: object
        currency:
          description: The rates' currency
          type: string
        delivery:
          additionalProperties:
            additionalProperties:
              additionalProperties:
                format: int64
                type: integer
              type: object
            type: object
          description: By option, size and the buyer's zone, in minor units; missing means quoted
          type: object
        free_storage_weeks:
          description: Weeks of storage before the fee starts
          format: int64
          maximum: 104
          minimum: 0
          type: integer
        options:
          description: Delivery services, e.g. standard, white glove
          items:
            $ref: "#/components/schemas/ShippingOptionBody"
          type:
            - array
            - "null"
        sizes:
          items:
            $ref: "#/components/schemas/ShippingSizeBody"
          type:
            - array
            - "null"
        storage_fees:
          additionalProperties:
            format: int64
            type: integer
          description: Per started month, by size, taken from the seller's payout
          type: object
        zones:
          items:
            $ref: "#/components/schemas/ShippingZoneBody"
          type:
            - array
            - "null"
      required:
        - sizes
        - zones
      type: object
    ShippingOptionBody:
      additionalProperties: false
      properties:
        about:
          type: string
        key:
          type: string
        name:
          type: string
      required:
        - key
        - name
      type: object
    ShippingSizeBody:
      additionalProperties: false
      properties:
        about:
          type: string
        key:
          type: string
        name:
          type: string
      required:
        - key
        - name
      type: object
    ShippingZoneBody:
      additionalProperties: false
      properties:
        countries:
          description: ISO codes, e.g. ["SE"]
          items:
            type: string
          type:
            - array
            - "null"
        delivery_time:
          description: Shown to buyers, e.g. 2–5 working days
          type: string
        key:
          type: string
        name:
          type: string
        postal_prefixes:
          description: "None: the whole countries"
          items:
            type: string
          type:
            - array
            - "null"
      required:
        - key
        - name
        - countries
      type: object
    SignInInputBody:
      additionalProperties: false
      properties:
        email:
          maxLength: 254
          type: string
        password:
          maxLength: 128
          type: string
      required:
        - email
        - password
      type: object
    SignUpInputBody:
      additionalProperties: false
      properties:
        bio:
          maxLength: 2000
          type: string
        display_name:
          maxLength: 100
          type: string
        email:
          format: email
          maxLength: 254
          type: string
        first_name:
          maxLength: 100
          type: string
        last_name:
          maxLength: 100
          type: string
        locale:
          description: One of the marketplace's languages
          type: string
        password:
          description: "Optional: without one, the user signs in with emailed links"
          maxLength: 128
          type: string
        phone:
          maxLength: 40
          type: string
      required:
        - email
      type: object
    SignedInOutputBody:
      additionalProperties: false
      properties:
        session:
          $ref: "#/components/schemas/SessionBody"
        user:
          $ref: "#/components/schemas/UserBody"
      required:
        - user
        - session
      type: object
    SiteBody:
      additionalProperties: false
      properties:
        active:
          type: boolean
        buyer_pickup:
          description: Buyers may collect purchases here
          type: boolean
        city:
          type: string
        country:
          type: string
        created_at:
          format: date-time
          type: string
        id:
          type: string
        kind:
          enum:
            - showroom
            - warehouse
          type: string
        line1:
          type: string
        name:
          type: string
        opening_hours:
          description: Free text, e.g. Sundays 11–16
          type: string
        postal_code:
          type: string
      required:
        - id
        - name
        - kind
        - line1
        - postal_code
        - city
        - country
        - buyer_pickup
        - active
        - created_at
      type: object
    SiteRefBody:
      additionalProperties: false
      properties:
        buyer_pickup:
          description: Buyers may collect purchases here
          type: boolean
        city:
          type: string
        id:
          type: string
        kind:
          enum:
            - showroom
            - warehouse
          type: string
        name:
          type: string
      required:
        - id
        - name
        - kind
      type: object
    SlotBody:
      additionalProperties: false
      properties:
        date:
          description: YYYY-MM-DD
          type: string
        full:
          type: boolean
        label:
          description: e.g. 08–12
          type: string
        window:
          type: string
      required:
        - date
        - window
        - label
      type: object
    SlotInputBody:
      additionalProperties: false
      properties:
        date:
          description: YYYY-MM-DD
          examples:
            - 2026-10-28
          pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}$
          type: string
        window:
          examples:
            - am
          maxLength: 40
          type: string
      required:
        - date
        - window
      type: object
    StartConversationRequest:
      additionalProperties: false
      properties:
        body:
          maxLength: 5000
          minLength: 1
          type: string
        listing_id:
          type: string
        order_id:
          type: string
        support:
          description: To the marketplace
          type: boolean
      required:
        - body
      type: object
    StartConversationResponse:
      additionalProperties: false
      properties:
        conversation:
          $ref: "#/components/schemas/ConversationBody"
        message:
          $ref: "#/components/schemas/MessageBody"
      required:
        - conversation
        - message
      type: object
    StartMyPayoutSetupRequest:
      additionalProperties: false
      properties:
        business_type:
          description: Private person (default) or company; used the first time
          enum:
            - individual
            - company
          type: string
        country:
          description: Where the seller lives (ISO 3166, e.g. SE); used the first time
          examples:
            - SE
          pattern: ^[A-Za-z]{2}$
          type: string
      required:
        - country
      type: object
    StartMyPayoutSetupResponse:
      additionalProperties: false
      properties:
        client_secret:
          type: string
      required:
        - client_secret
      type: object
    SubscribeNewsletterRequest:
      additionalProperties: false
      properties:
        email:
          format: email
          maxLength: 254
          type: string
        first_name:
          maxLength: 100
          type: string
        locale:
          maxLength: 35
          type: string
        source:
          description: Where they signed up, e.g. home or footer
          maxLength: 60
          type: string
      required:
        - email
      type: object
    TermBody:
      additionalProperties: false
      properties:
        description:
          type: string
        id:
          type: string
        kind:
          type: string
        listing_count:
          description: Listings on sale with this term (with in_use)
          format: int64
          type: integer
        name:
          type: string
        slug:
          type: string
      required:
        - id
        - kind
        - slug
        - name
      type: object
    TermKindBody:
      additionalProperties: false
      properties:
        key:
          type: string
        names:
          additionalProperties:
            type: string
          description: Per language
          type: object
      required:
        - key
        - names
      type: object
    TermRefBody:
      additionalProperties: false
      properties:
        descriptions:
          additionalProperties:
            type: string
          description: About the brand, designer…, by locale (e.g. en, sv); on single listings only
          type: object
        id:
          type: string
        name:
          type: string
        slug:
          type: string
      required:
        - id
        - slug
        - name
      type: object
    Tile:
      additionalProperties: false
      properties:
        category:
          $ref: "#/components/schemas/CategoryRef"
          readOnly: true
        category_id:
          type: string
        image:
          $ref: "#/components/schemas/ImageRef"
          readOnly: true
        image_id:
          type: string
        label:
          description: Shown instead of the category's name
          type: string
      required:
        - category_id
      type: object
    TokenInputBody:
      additionalProperties: false
      properties:
        token:
          description: From the emailed link's ?token=
          maxLength: 200
          type: string
      required:
        - token
      type: object
    TransitionInputBody:
      additionalProperties: false
      properties:
        data:
          additionalProperties:
            type: string
          description: E.g. tracking_number and carrier when marking delivered
          type: object
      type: object
    UpdateMeInputBody:
      additionalProperties: false
      properties:
        bio:
          maxLength: 2000
          type: string
        display_name:
          maxLength: 100
          type: string
        first_name:
          maxLength: 100
          type: string
        last_name:
          maxLength: 100
          type: string
        locale:
          description: One of the marketplace's languages, or empty for its default
          type: string
        phone:
          maxLength: 40
          type: string
      type: object
    UserBody:
      additionalProperties: false
      properties:
        bio:
          description: Public
          type: string
        created_at:
          format: date-time
          type: string
        display_name:
          type: string
        email:
          format: email
          type: string
        email_verified:
          type: boolean
        first_name:
          type: string
        has_password:
          type: boolean
        id:
          type: string
        last_name:
          type: string
        locale:
          description: The user's language; empty means the marketplace's default
          type: string
        phone:
          description: Private
          type: string
      required:
        - id
        - email
        - email_verified
        - has_password
        - first_name
        - last_name
        - display_name
        - bio
        - phone
        - locale
        - created_at
      type: object
    ErrorDetail:
      additionalProperties: false
      properties:
        location:
          description: Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'
          type: string
        message:
          description: Error message text
          type: string
        value:
          description: The value at the given location
      type: object
  securitySchemes:
    apiKey:
      description: "API key: `Authorization: Bearer sk_live_…` (secret, servers) or `pk_live_…` (publishable, browsers)."
      scheme: bearer
      type: http
    userSession:
      description: A signed-in user's session token, sent by the shop's server together with its secret key.
      in: header
      name: X-User-Session
      type: apiKey
