openapi: 3.1.0
info:
  title: Mascot Public API
  version: 1.0.0
  summary: Public API for inventory, commerce, shipping, and event integrations.
  description: |
    The Mascot Public API lets an authorized collector manage inventory, publish listings,
    process bulk operations, discover and purchase products, manage offers, retrieve sales,
    purchase shipping labels, and receive signed webhooks.

    All `/api/public/v1` operations require both `api_key` and `auth_token` headers. Each
    operation also identifies the scope required by the auth token.

    See the integration guides for prerequisites and complete call sequences. This reference
    defines only the public HTTP contract.
  contact:
    name: Mascot Integrations
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
servers:
  - url: https://app-api.withmascot.com
    description: Production
security:
  - ApiKey: []
    AuthToken: []
tags:
  - name: Marketplace connections
    description: Discover the collector's connected sales channels.
  - name: Items
    description: Manage a collector's inventory and listing lifecycle.
  - name: Item feeds
    description: Submit and monitor asynchronous bulk inventory operations.
  - name: Products
    description: Discover products offered by other collectors.
  - name: Purchases
    description: Buy products and retrieve buyer orders.
  - name: Offers
    description: Submit offers and respond to seller counter-offers.
  - name: Sales
    description: Retrieve the authenticated collector's sales.
  - name: Shipping
    description: Purchase and manage shipping labels for eligible sales.
  - name: Webhook endpoints
    description: Configure destinations for signed event deliveries.
paths:
  /api/public/v1/marketplace-connections:
    get:
      tags:
        - Marketplace connections
      summary: List marketplace connections
      description: |
        Returns the collector's supported marketplace connections. Each connection has its own
        `connection_id`, so multiple accounts for the same marketplace are returned separately.
        Use these IDs in the item listing request. No marketplace credentials are exposed.
      operationId: findMarketplaceConnections
      x-required-scope: items:read
      responses:
        '200':
          description: Marketplace connections available to the collector.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/MarketplaceConnection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/marketplace-connections/{connectionId}/locations:
    parameters:
      - name: connectionId
        in: path
        required: true
        description: Concrete marketplace connection ID.
        schema:
          type: integer
          format: int64
          minimum: 1
    get:
      tags:
        - Marketplace connections
      summary: List locations for a marketplace connection
      description: |
        Returns locations that may be supplied when listing through this connection. Shopify,
        Square, and Walmart can return selectable locations. Connections without a location concept,
        including Mascot Network, return an empty array.
      operationId: findMarketplaceConnectionLocations
      x-required-scope: items:read
      responses:
        '200':
          description: Selectable locations for the connection.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/MarketplaceLocation'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/items:
    post:
      tags:
        - Items
      summary: Create an item
      description: |
        Enriches and persists one inventory item synchronously. Use an item feed for bulk creation.
        Graded and raw items have different identification prerequisites described by the request
        schema. Supplied image URLs are downloaded and copied to Mascot storage before the item is
        created. Creating an item does not publish it; call the listing endpoint separately.
      operationId: createItem
      x-required-scope: items:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateItemRequest'
            examples:
              graded:
                value:
                  type: GRADED
                  category: SPORTS_CARD
                  external_id: inventory-123
                  cert_number: '12345678'
                  authenticator: PSA
                  quantity: 1
                  item_price: 125
              raw:
                value:
                  type: RAW
                  category: TRADING_CARD_GAME
                  external_id: inventory-124
                  front_image_url: https://images.example.com/card-front.jpg
                  back_image_url: https://images.example.com/card-back.jpg
                  quantity: 1
                  item_price: 25
      responses:
        '201':
          description: Item created. Returns the same current item representation as the item detail endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Item'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
    get:
      tags:
        - Items
      summary: Find items
      description: |
        Returns in-stock, non-deleted items for the authenticated collector. Every result uses the
        same complete public item representation returned by the item detail endpoint.
      operationId: findItems
      x-required-scope: items:read
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: external_id
          in: query
          schema:
            type: string
            maxLength: 255
        - name: type
          in: query
          schema:
            $ref: '#/components/schemas/ItemType'
        - name: category
          in: query
          schema:
            $ref: '#/components/schemas/ItemCategory'
        - name: sku
          in: query
          schema:
            type: string
        - name: cert_number
          in: query
          schema:
            type: string
        - name: updated_after
          in: query
          description: Return items updated strictly after this ISO-8601 timestamp.
          schema:
            type: string
            format: date-time
        - name: tag
          in: query
          description: |
            Return items that have any of these tags. Repeat the parameter or separate values with
            commas. Matching is case-insensitive.
          schema:
            type: array
            items:
              type: string
              maxLength: 255
          style: form
          explode: true
        - name: is_listed
          in: query
          description: |
            When true, return items with a current non-ended marketplace listing. When false, return
            items that are not currently listed.
          schema:
            type: boolean
      responses:
        '200':
          description: Item page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindItemsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/items/{itemId}:
    parameters:
      - $ref: '#/components/parameters/ItemId'
    get:
      tags:
        - Items
      summary: Get an item
      description: Returns the current public representation of one non-deleted item owned by the collector.
      operationId: getItem
      x-required-scope: items:read
      responses:
        '200':
          description: Item detail.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Item'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
    patch:
      tags:
        - Items
      summary: Update an item
      description: |
        Applies a top-level JSON Merge Patch. Omitted fields remain unchanged, `null` clears an
        optional field, and arrays replace their previous value. Setting quantity to zero requests
        delisting automatically. Delisting completes asynchronously; observe `listing_status` or
        subscribe to item listing webhooks.
      operationId: updateItem
      x-required-scope: items:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateItemRequest'
            example:
              quantity: 2
              item_price: 130
      responses:
        '200':
          description: Updated item.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Item'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
    delete:
      tags:
        - Items
      summary: Delete an item
      description: |
        Soft-deletes the item and requests delisting. The item's external ID becomes reusable by
        this API key. A successful response means the delete request was accepted; marketplace
        delisting may still be in progress.
      operationId: deleteItem
      x-required-scope: items:write
      responses:
        '204':
          description: Item deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/items/{itemId}/listing:
    parameters:
      - $ref: '#/components/parameters/ItemId'
    post:
      tags:
        - Items
      summary: List an item
      description: |
        Requests publication through the explicitly selected marketplace connections. Obtain the
        connection IDs from `GET /api/public/v1/marketplace-connections`. Multiple connections for
        the same marketplace may be selected. The item must have a listing price greater than zero
        and available quantity. Publication is asynchronous; poll the item or use item listing
        webhooks to observe the result.
      operationId: listItem
      x-required-scope: items:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateItemListingRequest'
            example:
              connections:
                - connection_id: 42
                  location_id: '123456789'
                - connection_id: 57
      responses:
        '204':
          description: Listing request accepted.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
    delete:
      tags:
        - Items
      summary: Delist an item
      description: |
        Requests delisting from supported destinations where the item may still be available.
        Processing is asynchronous. While it is pending, the item reports `DELISTING`; the terminal
        state is available through the item API and item listing webhooks.
      operationId: delistItem
      x-required-scope: items:write
      parameters:
        - name: connections
          in: query
          required: false
          description: |
            Connection IDs to delist. Omit this parameter to delist the item from every current
            destination. Selecting Mascot Network also removes every listing distributed through it.
          style: form
          explode: false
          schema:
            type: array
            maxItems: 50
            uniqueItems: true
            items:
              type: integer
              format: int64
              minimum: 1
      responses:
        '204':
          description: Delisting request accepted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/item-feeds:
    post:
      tags:
        - Item feeds
      summary: Create an item feed
      description: |
        Submits a bulk `CREATE`, `UPDATE`, `DELETE`, `LIST`, or `DELIST` operation. The response is
        returned before task processing finishes. Store `feed_id`, then poll the feed until its
        status is `FINISHED` or subscribe to `item_feed.finished`.

        Feeds are visible only to the API key that created them, even when another API key belongs
        to the same collector.
      operationId: createItemFeed
      x-required-scope: items:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateItemFeedRequest'
            examples:
              create:
                value:
                  type: CREATE
                  items:
                    - type: GRADED
                      category: SPORTS_CARD
                      external_id: inventory-1001
                      cert_number: '12345678'
                      authenticator: PSA
              update:
                value:
                  type: UPDATE
                  items:
                    - item_id: 11111111-1111-1111-1111-111111111111
                      quantity: 0
              delist:
                value:
                  type: DELIST
                  item_ids:
                    - 11111111-1111-1111-1111-111111111111
                    - 22222222-2222-2222-2222-222222222222
      responses:
        '202':
          description: Feed accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ItemFeed'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/item-feeds/{feedId}:
    parameters:
      - $ref: '#/components/parameters/FeedId'
    get:
      tags:
        - Item feeds
      summary: Get an item feed
      description: Returns aggregate status and task counts for a feed created by this API key.
      operationId: getItemFeed
      x-required-scope: items:read
      responses:
        '200':
          description: Feed state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ItemFeed'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/item-feeds/{feedId}/tasks:
    parameters:
      - $ref: '#/components/parameters/FeedId'
    get:
      tags:
        - Item feeds
      summary: Find item feed tasks
      description: |
        Returns feed tasks in their original request order. Inspect tasks with status `ERROR` after
        the feed finishes. Task errors do not cause successful tasks to roll back.
      operationId: findItemFeedTasks
      x-required-scope: items:read
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: Task page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindItemFeedTasksResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/products:
    get:
      tags:
        - Products
      summary: Find products
      description: |
        Searches products that are actively listed, in stock, and available for you to purchase.
        Your own listings are excluded, so an empty `products` array is returned when nothing else
        currently matches.

        Results use cursor pagination over a snapshot taken when the first page is requested. To
        retrieve the next page, send `next_cursor` unchanged as `cursor` and repeat every filter,
        sort, and direction from the original request.
      operationId: findProducts
      x-required-scope: products:read
      parameters:
        - name: cursor
          in: query
          description: Opaque cursor returned by the previous page.
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: query
          in: query
          description: Full-text product search.
          schema:
            type: string
        - name: sort
          in: query
          schema:
            type: string
            enum:
              - relevance
              - newest
              - price
              - year
              - grade
              - title
          description: Defaults to `relevance` when `query` is supplied and `newest` otherwise.
        - name: direction
          in: query
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
        - name: price_min
          in: query
          schema:
            type: number
            minimum: 0
        - name: price_max
          in: query
          schema:
            type: number
            minimum: 0
        - name: year_min
          in: query
          schema:
            type: integer
        - name: year_max
          in: query
          schema:
            type: integer
        - name: categories
          in: query
          description: Comma-separated category names.
          schema:
            type: string
        - name: graded
          in: query
          schema:
            type: boolean
        - name: graders
          in: query
          description: Comma-separated grader names.
          schema:
            type: string
        - name: grade_min
          in: query
          schema:
            type: number
        - name: grade_max
          in: query
          schema:
            type: number
        - name: verified_cert
          in: query
          schema:
            type: boolean
        - name: sport
          in: query
          schema:
            type: string
        - name: game
          in: query
          schema:
            type: string
        - name: manufacturer
          in: query
          schema:
            type: string
        - name: subject
          in: query
          schema:
            type: string
        - name: cert_number
          in: query
          schema:
            type: string
        - name: upc
          in: query
          schema:
            type: string
        - name: serial_numbered
          in: query
          schema:
            type: boolean
        - name: offers_allowed
          in: query
          schema:
            type: boolean
        - name: has_shipping_discount
          in: query
          description: Filter by whether a lower combined-shipping total is currently available.
          schema:
            type: boolean
      responses:
        '200':
          description: Product page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindProductsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/products/{productId}:
    parameters:
      - $ref: '#/components/parameters/ProductId'
    get:
      tags:
        - Products
      summary: Get a product
      description: |
        Returns a product that is actively listed, in stock, and available for you to purchase.
        Your own listings return `404`.
      operationId: getProduct
      x-required-scope: products:read
      responses:
        '200':
          description: Product detail.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/purchases:
    post:
      tags:
        - Purchases
      summary: Buy a product
      description: |
        Purchases one unit using the collector's saved payment method. The buyer must have a usable
        saved payment method and a default shipping address, and the product must still be available.

        `external_id` makes the request idempotent for this API key. Repeating the same external ID
        with the same product returns the existing result. Reusing it for another product returns
        `409 idempotency_conflict`.

        When compatible with a previous order whose shipping label has not been created, Mascot may
        combine shipping automatically. The returned order and totals are authoritative.
      operationId: createPurchase
      x-required-scope: purchases:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePurchaseRequest'
            example:
              external_id: purchase-1001
              product_id: 018f3a7e-0df8-7a40-a918-2d728f60c98f
      responses:
        '200':
          description: Purchase confirmed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Purchase'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
    get:
      tags:
        - Purchases
      summary: Find purchases
      description: Returns purchases made by the authenticated collector, grouped by Mascot order.
      operationId: findPurchases
      x-required-scope: purchases:read
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: Purchase page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindPurchasesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/purchases/{orderId}:
    get:
      tags:
        - Purchases
      summary: Get a purchase
      description: Returns a buyer order, including its current shipping and tracking details.
      operationId: getPurchase
      x-required-scope: purchases:read
      parameters:
        - name: orderId
          in: path
          required: true
          description: Mascot order ID returned by a buy or accepted-offer response.
          schema:
            type: string
      responses:
        '200':
          description: Purchase detail.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Purchase'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/offers:
    post:
      tags:
        - Offers
      summary: Create an offer
      description: |
        Submits a buyer offer for an available product that allows offers. `external_id` is
        idempotent for this API key. Reusing it with the same product and amount returns the current
        result; reusing it with different input returns `409 idempotency_conflict`.

        Most successful requests return an offer. If the offer completes a purchase immediately,
        the response is the same purchase payload returned by the buy endpoint.
      operationId: createOffer
      x-required-scope: offers:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOfferRequest'
            example:
              external_id: offer-1001
              product_id: 018f3a7e-0df8-7a40-a918-2d728f60c98f
              amount: 110
      responses:
        '200':
          description: Offer recorded or purchase completed.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/Offer'
                  - $ref: '#/components/schemas/Purchase'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
    get:
      tags:
        - Offers
      summary: Find offers
      description: Returns offer threads belonging to the authenticated buyer, with the latest state of each thread.
      operationId: findOffers
      x-required-scope: offers:read
      parameters:
        - name: status
          in: query
          description: Set to `ACTIVE` to return only pending, unexpired offer threads.
          schema:
            type: string
            enum:
              - ACTIVE
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: Offer page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindOffersResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/offers/{offerId}:
    parameters:
      - $ref: '#/components/parameters/OfferId'
    get:
      tags:
        - Offers
      summary: Get an offer
      description: Returns the latest state in the requested buyer-owned offer thread.
      operationId: getOffer
      x-required-scope: offers:read
      responses:
        '200':
          description: Latest offer state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Offer'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/offers/{offerId}/accept:
    parameters:
      - $ref: '#/components/parameters/OfferId'
    post:
      tags:
        - Offers
      summary: Accept a seller counter-offer
      description: |
        Accepts the latest pending seller counter-offer and purchases one unit using the saved
        payment method. Buyers cannot submit another counter-offer; they can only accept or reject
        the seller's counter. The buyer must have a usable saved payment method and default shipping
        address. `external_id` makes the action idempotent.
      operationId: acceptOffer
      x-required-scope: offers:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OfferActionRequest'
            example:
              external_id: accept-offer-1001
      responses:
        '200':
          description: Counter-offer accepted and purchase completed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Purchase'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/offers/{offerId}/reject:
    parameters:
      - $ref: '#/components/parameters/OfferId'
    post:
      tags:
        - Offers
      summary: Reject a seller counter-offer
      description: Rejects the latest pending seller counter-offer. `external_id` makes the action idempotent.
      operationId: rejectOffer
      x-required-scope: offers:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OfferActionRequest'
            example:
              external_id: reject-offer-1001
      responses:
        '200':
          description: Counter-offer rejected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Offer'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/sales:
    get:
      tags:
        - Sales
      summary: Find sales
      description: |
        Returns sales belonging to the authenticated seller. Each sale includes public order state,
        sold items, shipping address, and shipment tracking when available. The payload is the same
        public sale representation used by sale and shipment webhooks.
      operationId: findSales
      x-required-scope: sales:read
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: direction
          in: query
          description: Sort direction by order date.
          schema:
            type: string
            enum:
              - ASC
              - DESC
            default: DESC
      responses:
        '200':
          description: Sale page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindSalesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/shipping/ship-from-locations:
    get:
      tags:
        - Shipping
      summary: Find ship-from locations
      description: Returns the collector's ship-from locations. Use an ID from this response when selecting a non-default origin for a shipping batch.
      operationId: findShipFromLocations
      x-required-scope: shipping:read
      responses:
        '200':
          description: Ship-from locations.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ShipFromLocation'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/shipping/eligible-sales:
    get:
      tags:
        - Shipping
      summary: Find sales eligible for shipping
      description: |
        Returns sales that can be considered for batch label creation from the selected ship-from
        location. Batch shipping must be enabled for the collector. When no location is supplied,
        Mascot uses the default ship-from location, or the first available location.

        Check `ready_for_shipping` immediately before submitting a batch; eligibility can change.
      operationId: findSalesEligibleForShipping
      x-required-scope: shipping:read
      parameters:
        - name: ship_from_location_id
          in: query
          schema:
            type: string
            format: uuid
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: Eligible sale page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindEligibleSalesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/shipping-batches:
    post:
      tags:
        - Shipping
      summary: Create a shipping batch
      description: |
        Starts asynchronous label creation for eligible sales. Batch shipping must be enabled and
        the collector must have a ship-from location. Every selected sale must currently report
        `ready_for_shipping: true`.

        `external_id` is idempotent for this API key and collector. Repeating the same external ID
        with the same normalized sale IDs, location, and ship date returns the existing batch.
        Reusing it for different input returns `409 conflict`.

        Poll the returned batch until it reaches `FINISHED`, `ACTION_REQUIRED`, or `FAILED`, or
        subscribe to the corresponding shipping-batch webhook events.
      operationId: createShippingBatch
      x-required-scope: shipping:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateShippingBatchRequest'
            example:
              external_id: shipping-batch-1001
              sale_ids:
                - aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
                - bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb
              ship_from_location_id: 018f3a7e-0df8-7a40-a918-2d728f60c98f
              ship_date: '2026-08-24'
      responses:
        '202':
          description: Shipping batch accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShippingBatch'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
    get:
      tags:
        - Shipping
      summary: Find shipping batches
      description: Returns the collector's shipping batches, newest first.
      operationId: findShippingBatches
      x-required-scope: shipping:read
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: status
          in: query
          schema:
            $ref: '#/components/schemas/ShippingBatchStatus'
        - name: date_from
          in: query
          schema:
            type: string
            format: date
        - name: date_to
          in: query
          description: Must be on or after `date_from`.
          schema:
            type: string
            format: date
        - name: ship_from_location_id
          in: query
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Shipping batch page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindShippingBatchesResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/shipping-batches/{batchId}:
    parameters:
      - $ref: '#/components/parameters/BatchId'
    get:
      tags:
        - Shipping
      summary: Get a shipping batch
      description: Returns aggregate state, each shipment result, and the documents currently available for the batch.
      operationId: getShippingBatch
      x-required-scope: shipping:read
      responses:
        '200':
          description: Shipping batch detail.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShippingBatchDetail'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
  /api/public/v1/shipping-batches/{batchId}/retry:
    parameters:
      - $ref: '#/components/parameters/BatchId'
    post:
      tags:
        - Shipping
      summary: Retry a shipping batch
      description: |
        Creates a new asynchronous batch for recoverable failed shipments. The original batch must
        contain failed shipments and use today's ship date. Use a new `external_id` for the retry.
        If the ship date is no longer today, submit a new batch from the eligible-sales endpoint.
      operationId: retryShippingBatch
      x-required-scope: shipping:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RetryShippingBatchRequest'
            example:
              external_id: shipping-retry-1001
      responses:
        '202':
          description: Retry batch accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShippingBatch'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/shipping-batches/{batchId}/remove-failed-items:
    parameters:
      - $ref: '#/components/parameters/BatchId'
    post:
      tags:
        - Shipping
      summary: Remove failed items from a shipping batch
      description: |
        Completes a partially successful batch by removing its failed shipments and regenerating
        the remaining documents. At least one label must have succeeded. If every shipment failed,
        retry the batch instead.
      operationId: removeFailedShippingBatchItems
      x-required-scope: shipping:write
      responses:
        '202':
          description: Failed items removed and document regeneration accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShippingBatch'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/shipping-batches/{batchId}/labels:
    parameters:
      - $ref: '#/components/parameters/BatchId'
    get:
      tags:
        - Shipping
      summary: Get shipping labels
      description: Returns a temporary download URL after `labels_available` becomes true.
      operationId: getShippingLabels
      x-required-scope: shipping:read
      responses:
        '200':
          description: Labels document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShippingDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/public/v1/shipping-batches/{batchId}/pick-list:
    parameters:
      - $ref: '#/components/parameters/BatchId'
    get:
      tags:
        - Shipping
      summary: Get a shipping pick list
      description: Returns a temporary download URL after `pick_list_available` becomes true.
      operationId: getShippingPickList
      x-required-scope: shipping:read
      responses:
        '200':
          description: Pick-list document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShippingDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/public/v1/shipping-batches/{batchId}/manifest:
    parameters:
      - $ref: '#/components/parameters/BatchId'
    get:
      tags:
        - Shipping
      summary: Get a shipping manifest
      description: Returns a temporary download URL after `manifest_available` becomes true.
      operationId: getShippingManifest
      x-required-scope: shipping:read
      responses:
        '200':
          description: Manifest document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShippingDocument'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/public/v1/webhook-endpoints:
    post:
      tags:
        - Webhook endpoints
      summary: Create a webhook endpoint
      description: |
        Registers a public HTTPS URL and the events it should receive. The API key must have the
        read scope associated with every requested event type. The response returns the signing
        `secret` exactly once; store it securely before discarding the response.

        Webhook endpoints belong to the API key that creates them.
      operationId: createWebhookEndpoint
      x-required-scope: webhooks:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEndpointRequest'
            example:
              url: https://integration.example.com/webhooks/mascot
              event_types:
                - item.updated
                - sale.created
                - purchase.updated
      responses:
        '201':
          description: Webhook endpoint created. This response contains the new signing secret.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
    get:
      tags:
        - Webhook endpoints
      summary: Find webhook endpoints
      description: Returns webhook endpoints created by this API key. Signing secrets are never returned by read operations.
      operationId: findWebhookEndpoints
      x-required-scope: webhooks:read
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
      responses:
        '200':
          description: Webhook endpoint page.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindWebhookEndpointsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/webhook-endpoints/{id}:
    parameters:
      - $ref: '#/components/parameters/EndpointId'
    get:
      tags:
        - Webhook endpoints
      summary: Get a webhook endpoint
      description: Returns endpoint configuration and delivery health, without the signing secret.
      operationId: getWebhookEndpoint
      x-required-scope: webhooks:read
      responses:
        '200':
          description: Webhook endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
    put:
      tags:
        - Webhook endpoints
      summary: Replace a webhook endpoint configuration
      description: Replaces the URL and event selection. This operation does not rotate the signing secret.
      operationId: updateWebhookEndpoint
      x-required-scope: webhooks:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEndpointRequest'
      responses:
        '200':
          description: Webhook endpoint updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
    delete:
      tags:
        - Webhook endpoints
      summary: Disable a webhook endpoint
      description: Disables the endpoint. It remains visible and can later be enabled again.
      operationId: deleteWebhookEndpoint
      x-required-scope: webhooks:write
      responses:
        '200':
          description: Disabled webhook endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/webhook-endpoints/{id}/disable:
    parameters:
      - $ref: '#/components/parameters/EndpointId'
    post:
      tags:
        - Webhook endpoints
      summary: Disable a webhook endpoint
      description: Stops future deliveries to this endpoint until it is enabled again.
      operationId: disableWebhookEndpoint
      x-required-scope: webhooks:write
      responses:
        '200':
          description: Disabled webhook endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/webhook-endpoints/{id}/enable:
    parameters:
      - $ref: '#/components/parameters/EndpointId'
    post:
      tags:
        - Webhook endpoints
      summary: Enable a webhook endpoint
      description: Revalidates the endpoint URL and resumes delivery of new events.
      operationId: enableWebhookEndpoint
      x-required-scope: webhooks:write
      responses:
        '200':
          description: Enabled webhook endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/public/v1/webhook-endpoints/{id}/rotate-secret:
    parameters:
      - $ref: '#/components/parameters/EndpointId'
    post:
      tags:
        - Webhook endpoints
      summary: Rotate a webhook signing secret
      description: Replaces the endpoint's signing secret. The response returns the new secret exactly once and subsequent deliveries use it.
      operationId: rotateWebhookEndpointSecret
      x-required-scope: webhooks:write
      responses:
        '200':
          description: Secret rotated. This response contains the new signing secret.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpoint'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
webhooks:
  event:
    post:
      tags:
        - Webhook events
      summary: Receive a Mascot webhook event
      description: |
        Mascot sends the event to the URL configured on the webhook endpoint. Calculate
        `HMAC-SHA256(secret, timestamp + "." + raw_request_body)` and compare it with the hexadecimal
        value in `Mascot-Webhook-Signature`. Verify the signature using the raw bytes before parsing
        JSON, compare in constant time, and reject stale timestamps.

        Return any `2xx` status after durably accepting the event. Delivery is at least once, so
        consumers must deduplicate using the event ID. A `410 Gone` response disables the endpoint.
        Other non-`2xx` responses and network failures are retried up to ten attempts over 72 hours.
      operationId: receiveWebhookEvent
      security: []
      parameters:
        - name: Mascot-Event-Id
          in: header
          required: true
          schema:
            type: string
        - name: Mascot-Event-Type
          in: header
          required: true
          schema:
            $ref: '#/components/schemas/WebhookEventType'
        - name: Mascot-Delivery-Id
          in: header
          required: true
          schema:
            type: string
        - name: Mascot-Delivery-Attempt
          in: header
          required: true
          schema:
            type: integer
            minimum: 1
        - name: Mascot-Webhook-Timestamp
          in: header
          required: true
          schema:
            type: string
        - name: Mascot-Webhook-Signature
          in: header
          required: true
          schema:
            type: string
            pattern: ^v1=[a-f0-9]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEnvelope'
      responses:
        '200':
          description: Event accepted.
        '204':
          description: Event accepted with no response body.
        '410':
          description: Endpoint is permanently unavailable and should be disabled.
components:
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: api_key
      description: Integration API key issued by Mascot.
    AuthToken:
      type: apiKey
      in: header
      name: auth_token
      description: Collector auth token issued by Mascot.
  schemas:
    Error:
      type: object
      additionalProperties: false
      required:
        - error
        - message
      properties:
        error:
          type: string
          description: Stable machine-readable error code.
          examples:
            - bad_request
        message:
          type: string
          description: Human-readable description of the failure.
          examples:
            - The request is invalid.
    Item:
      allOf:
        - $ref: '#/components/schemas/ItemWritableFields'
        - type: object
          required:
            - item_id
            - external_id
            - type
            - listing_status
            - created_at
            - updated_at
          properties:
            type:
              $ref: '#/components/schemas/ItemType'
            external_id:
              type: string
              minLength: 1
              maxLength: 255
            cert_number:
              type:
                - string
                - 'null'
            authenticator:
              oneOf:
                - $ref: '#/components/schemas/Authenticator'
                - type: 'null'
            item_id:
              type: string
              format: uuid
            listing_status:
              $ref: '#/components/schemas/ItemListingStatus'
            total_cost:
              allOf:
                - $ref: '#/components/schemas/NonNegativeAmount'
              readOnly: true
              description: Item cost plus extra costs such as tax, shipping, grading, and buyer's premium.
            listings:
              type: array
              readOnly: true
              description: Current non-ended marketplace listings. Included in the item detail response.
              items:
                $ref: '#/components/schemas/ItemMarketplaceListing'
            created_at:
              type: string
              format: date-time
            updated_at:
              type: string
              format: date-time
            title:
              type:
                - string
                - 'null'
              readOnly: true
              description: Current display title generated from the stored collectible data.
            subtitle:
              type:
                - string
                - 'null'
              readOnly: true
            quantity_available:
              type:
                - integer
                - 'null'
              readOnly: true
            quantity_sold:
              type:
                - integer
                - 'null'
              readOnly: true
            storage_location:
              type:
                - string
                - 'null'
              readOnly: true
            templated_marketplace_title:
              type:
                - string
                - 'null'
              readOnly: true
            templated_marketplace_description:
              type:
                - string
                - 'null'
              readOnly: true
            aspects:
              type:
                - array
                - 'null'
              readOnly: true
              items:
                $ref: '#/components/schemas/ItemAspect'
            inventory:
              type:
                - array
                - 'null'
              readOnly: true
              description: Inventory entries without internal inventory or invoice identifiers.
              items:
                $ref: '#/components/schemas/ItemInventoryEntry'
            recent_comp:
              oneOf:
                - $ref: '#/components/schemas/ItemRecentComp'
                - type: 'null'
              readOnly: true
            vcp_pricing:
              oneOf:
                - $ref: '#/components/schemas/ItemVcpPricing'
                - type: 'null'
              readOnly: true
            vcp_sync_status:
              type:
                - string
                - 'null'
              readOnly: true
            waxstat_slug:
              type:
                - string
                - 'null'
              readOnly: true
            waxstat_url:
              type:
                - string
                - 'null'
              format: uri
              readOnly: true
            parent_item_id:
              type:
                - string
                - 'null'
              format: uuid
              readOnly: true
            auto_repricing:
              oneOf:
                - $ref: '#/components/schemas/ItemAutoRepricing'
                - type: 'null'
              readOnly: true
            mba_certification:
              oneOf:
                - $ref: '#/components/schemas/ItemMbaCertification'
                - type: 'null'
              readOnly: true
      unevaluatedProperties: false
    CreateItemRequest:
      oneOf:
        - title: Graded item
          allOf:
            - $ref: '#/components/schemas/ItemWritableFields'
            - type: object
              required:
                - type
                - external_id
                - cert_number
              properties:
                type:
                  const: GRADED
                external_id:
                  type: string
                  minLength: 1
                  maxLength: 255
                  description: Identifier assigned by the integration. It must be unique among non-deleted items for this API key.
                cert_number:
                  type: string
                  pattern: ^[A-Za-z0-9-]{6,13}$
                authenticator:
                  allOf:
                    - $ref: '#/components/schemas/Authenticator'
                  description: When omitted, Mascot uses the grader configured for the integration or detects it when possible.
          unevaluatedProperties: false
        - title: Raw item
          allOf:
            - $ref: '#/components/schemas/ItemWritableFields'
            - type: object
              required:
                - type
                - external_id
                - front_image_url
                - back_image_url
              properties:
                type:
                  const: RAW
                external_id:
                  type: string
                  minLength: 1
                  maxLength: 255
                  description: Identifier assigned by the integration. It must be unique among non-deleted items for this API key.
                front_image_url:
                  $ref: '#/components/schemas/ImageUrl'
                back_image_url:
                  $ref: '#/components/schemas/ImageUrl'
          unevaluatedProperties: false
      description: |
        Create requests require `external_id` and `type`. A `GRADED` item requires a valid
        `cert_number`. A `RAW` item requires both `front_image_url` and `back_image_url`. Create
        quantity, when supplied, must be greater than zero. Supplied images must be publicly
        downloadable JPEG, PNG, GIF, BMP, or WebP files no larger than 15 MB each. Mascot copies
        these images to its own storage; item responses contain the stored Mascot URLs.
    UpdateItemRequest:
      allOf:
        - $ref: '#/components/schemas/ItemWritableFields'
      minProperties: 1
      unevaluatedProperties: false
      description: |
        JSON Merge Patch document. Omitted fields are unchanged, `null` removes an optional value,
        and arrays replace their previous values. `type`, `external_id`, `cert_number`, and
        `authenticator` are immutable. Quantity may be zero; setting it to zero also requests
        delisting.
    ItemFeed:
      type: object
      additionalProperties: false
      required:
        - feed_id
        - type
        - status
        - total_count
        - success_count
        - pending_count
        - error_count
        - created_at
        - updated_at
      properties:
        feed_id:
          type: string
          format: uuid
        type:
          $ref: '#/components/schemas/ItemFeedType'
        status:
          $ref: '#/components/schemas/ItemFeedStatus'
        total_count:
          type: integer
          minimum: 1
        success_count:
          type: integer
          minimum: 0
        pending_count:
          type: integer
          minimum: 0
        error_count:
          type: integer
          minimum: 0
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Product:
      type: object
      additionalProperties: false
      required:
        - id
        - title
        - identifiers
        - buy_now_price
        - shipping
        - offers_allowed
        - aspects
        - images
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
          description: Public product title.
        identifiers:
          type: object
          additionalProperties: false
          required:
            - cert_number
            - upc
          properties:
            cert_number:
              type:
                - string
                - 'null'
            upc:
              type:
                - string
                - 'null'
        buy_now_price:
          allOf:
            - $ref: '#/components/schemas/Money'
          description: Item amount charged when purchasing this product immediately.
        shipping:
          type: object
          additionalProperties: false
          required:
            - price
            - discount
            - total
            - can_combine
          properties:
            price:
              $ref: '#/components/schemas/Amount'
            discount:
              $ref: '#/components/schemas/Amount'
            total:
              allOf:
                - $ref: '#/components/schemas/Amount'
              description: Shipping amount for this product under the current buyer context.
            can_combine:
              type: boolean
              description: Whether this purchase can currently join an existing unlabeled order.
        offers_allowed:
          type: boolean
        aspects:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - aspect_id
              - aspect_name
              - values
            properties:
              aspect_id:
                type: integer
                format: int64
              aspect_name:
                type: string
              values:
                type: array
                items:
                  type: string
        images:
          type: object
          additionalProperties: false
          required:
            - front
            - back
          properties:
            front:
              type:
                - string
                - 'null'
              format: uri
            back:
              type:
                - string
                - 'null'
              format: uri
    Purchase:
      type: object
      additionalProperties: false
      required:
        - order_id
        - status
        - items
        - totals
        - shipping
        - created_at
        - updated_at
      properties:
        order_id:
          type:
            - string
            - 'null'
          description: Mascot order identifier. Use this value to retrieve the purchase.
        status:
          type: string
          enum:
            - PROCESSING
            - CONFIRMED
            - SHIPPED
            - DELIVERED
            - CANCELLED
        items:
          type: array
          minItems: 1
          items:
            type: object
            additionalProperties: false
            required:
              - product_id
              - external_id
              - title
              - unit_price
              - currency
            properties:
              product_id:
                type: string
                format: uuid
              external_id:
                type: string
              title:
                type: string
              unit_price:
                $ref: '#/components/schemas/Amount'
              currency:
                $ref: '#/components/schemas/CurrencyCode'
        totals:
          type: object
          additionalProperties: false
          required:
            - items_total
            - shipping_price
            - shipping_discount
            - shipping_total
            - total
            - currency
          properties:
            items_total:
              $ref: '#/components/schemas/Amount'
            shipping_price:
              $ref: '#/components/schemas/Amount'
            shipping_discount:
              $ref: '#/components/schemas/Amount'
            shipping_total:
              $ref: '#/components/schemas/Amount'
            total:
              allOf:
                - $ref: '#/components/schemas/Amount'
              description: Total buyer amount for the returned order.
            currency:
              $ref: '#/components/schemas/CurrencyCode'
        shipping:
          type: object
          additionalProperties: false
          required:
            - combined_shipping
            - combined_item_count
          properties:
            combined_shipping:
              type: boolean
            combined_item_count:
              type: integer
              minimum: 1
            tracking_number:
              type:
                - string
                - 'null'
            tracking_status:
              type:
                - string
                - 'null'
            tracking_url:
              type:
                - string
                - 'null'
              format: uri
            carrier:
              type:
                - string
                - 'null'
            service:
              type:
                - string
                - 'null'
            ship_to:
              oneOf:
                - $ref: '#/components/schemas/Address'
                - type: 'null'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Offer:
      type: object
      additionalProperties: false
      required:
        - id
        - thread_id
        - product_id
        - status
        - from_side
        - amount
        - expires_at
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
          description: Identifier of the latest offer message in this thread.
        thread_id:
          type: string
          format: uuid
          description: Stable identifier shared by the original offer and seller counter-offer.
        product_id:
          type: string
          format: uuid
        status:
          type: string
          enum:
            - PENDING
            - ACCEPTED
            - REJECTED
            - EXPIRED
            - CANCELLED
        from_side:
          type: string
          enum:
            - BUYER
            - SELLER
          description: Side that submitted the latest amount.
        amount:
          type: number
          minimum: 0
          multipleOf: 0.01
          description: Amount visible to the buyer.
        expires_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Sale:
      type: object
      additionalProperties: false
      required:
        - sale_id
        - marketplace
        - marketplace_fee
        - order_date
        - updated_at
        - paid
        - shipped
        - items
        - shipments
      properties:
        sale_id:
          type: string
          format: uuid
        marketplace:
          type: string
          description: Public sales channel shown to the seller.
        marketplace_fee:
          type: number
          minimum: 0
          multipleOf: 0.01
          description: Seller-visible marketplace charge, in the sale currency.
        payout:
          $ref: '#/components/schemas/SalePayout'
          description: Present when Mascot manages the seller payout for this sale.
        order_date:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        paid:
          type: boolean
        payment_received_at:
          type:
            - string
            - 'null'
          format: date-time
        shipped:
          type: boolean
        shipped_at:
          type:
            - string
            - 'null'
          format: date-time
        ship_by_date:
          type:
            - string
            - 'null'
          format: date-time
        cancelled_at:
          type:
            - string
            - 'null'
          format: date-time
        items:
          type: array
          items:
            type: object
            additionalProperties: false
            required:
              - item_id
              - external_id
              - description
              - quantity
              - unit_price
              - currency
            properties:
              item_id:
                type: string
                format: uuid
              external_id:
                type:
                  - string
                  - 'null'
                description: External item ID for the receiving API key, when one exists.
              description:
                type:
                  - string
                  - 'null'
              quantity:
                type: integer
                minimum: 1
              unit_price:
                type:
                  - number
                  - 'null'
                minimum: 0
              currency:
                type:
                  - string
                  - 'null'
              cancelled_at:
                type:
                  - string
                  - 'null'
                format: date-time
        shipments:
          type: array
          items:
            $ref: '#/components/schemas/SaleShipment'
    ShippingBatch:
      type: object
      additionalProperties: false
      required:
        - id
        - external_id
        - status
        - counts
        - ship_date
        - ship_from_location_id
        - labels_available
        - pick_list_available
        - manifest_available
        - retired
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
        external_id:
          type:
            - string
            - 'null'
        status:
          $ref: '#/components/schemas/ShippingBatchStatus'
        stage:
          $ref: '#/components/schemas/ShippingBatchStage'
        counts:
          $ref: '#/components/schemas/ShippingBatchCounts'
        ship_date:
          type: string
          format: date
        ship_from_location_id:
          type: string
          format: uuid
        ship_from_location_name:
          type:
            - string
            - 'null'
        labels_available:
          type: boolean
        pick_list_available:
          type: boolean
        manifest_available:
          type: boolean
        failure_message:
          type:
            - string
            - 'null'
        retired:
          type: boolean
          description: Whether this batch was superseded by a subsequent recovery action.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        finished_at:
          type:
            - string
            - 'null'
          format: date-time
    WebhookEndpoint:
      type: object
      additionalProperties: false
      required:
        - id
        - url
        - event_types
        - status
        - consecutive_failure_count
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
          format: uri
        event_types:
          type: array
          uniqueItems: true
          items:
            $ref: '#/components/schemas/WebhookEventType'
        status:
          $ref: '#/components/schemas/WebhookEndpointStatus'
        secret:
          type: string
          pattern: ^whsec_
          description: Returned only when the endpoint is created or its secret is rotated. Store it securely.
        consecutive_failure_count:
          type: integer
          minimum: 0
        last_success_at:
          type:
            - string
            - 'null'
          format: date-time
        last_failure_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    MarketplaceType:
      type: string
      enum:
        - EBAY
        - TIKTOK
        - SHOPIFY
        - SQUARE
        - MY_CARD_POST
        - WHATNOT
        - MASCOT_NETWORK
        - LOUPE
        - WALMART
    MarketplaceConnectionStatus:
      type: string
      enum:
        - READY
        - STARTED
        - EXPIRED
        - NOT_STARTED
      description: Current setup state of the connection. `READY` connections are fully configured.
    MarketplaceConnection:
      type: object
      additionalProperties: false
      required:
        - connection_id
        - marketplace
        - status
        - is_default
      properties:
        connection_id:
          type: integer
          format: int64
          minimum: 1
        marketplace:
          $ref: '#/components/schemas/MarketplaceType'
        nickname:
          type:
            - string
            - 'null'
        username:
          type:
            - string
            - 'null'
        status:
          $ref: '#/components/schemas/MarketplaceConnectionStatus'
        is_default:
          type: boolean
    MarketplaceLocation:
      type: object
      additionalProperties: false
      required:
        - id
      properties:
        id:
          type: string
        name:
          type:
            - string
            - 'null'
    ItemType:
      type: string
      enum:
        - GRADED
        - RAW
    ItemCategory:
      type: string
      enum:
        - SPORTS_CARD
        - TRADING_CARD_GAME
    ImageUrl:
      type: string
      format: uri
      maxLength: 2048
      pattern: ^https?://
    NonNegativeAmount:
      type: number
      format: float
      minimum: 0
      description: Non-negative amount in USD.
    ItemWritableFields:
      type: object
      properties:
        category:
          $ref: '#/components/schemas/ItemCategory'
        front_image_url:
          $ref: '#/components/schemas/ImageUrl'
        back_image_url:
          $ref: '#/components/schemas/ImageUrl'
        additional_image_urls:
          type: array
          description: Together with front and back, no more than eight unique image URLs are accepted.
          maxItems: 8
          uniqueItems: true
          items:
            $ref: '#/components/schemas/ImageUrl'
        item_cost:
          $ref: '#/components/schemas/NonNegativeAmount'
        item_price:
          $ref: '#/components/schemas/NonNegativeAmount'
        quantity:
          type: integer
          minimum: 0
        purchase_date:
          type: string
          format: date-time
        expected_delivery_date:
          type: string
          format: date-time
        item_received_date:
          type: string
          format: date-time
        purchased_from:
          type: string
          maxLength: 255
        buyer:
          type: string
          maxLength: 255
        location:
          type: string
          maxLength: 255
        sku:
          type: string
          maxLength: 255
        weight_lbs:
          $ref: '#/components/schemas/NonNegativeAmount'
        keep_if_out_of_stock:
          type: boolean
        buyers_premium_cost:
          $ref: '#/components/schemas/NonNegativeAmount'
        shipping_insurance_cost:
          $ref: '#/components/schemas/NonNegativeAmount'
        tax_cost:
          $ref: '#/components/schemas/NonNegativeAmount'
        grading_cost:
          $ref: '#/components/schemas/NonNegativeAmount'
        other_cost:
          $ref: '#/components/schemas/NonNegativeAmount'
        other_cost_description:
          type: string
          maxLength: 255
          description: Requires `other_cost` when provided.
        minimum_offer:
          $ref: '#/components/schemas/NonNegativeAmount'
        auto_accept_price:
          $ref: '#/components/schemas/NonNegativeAmount'
        auction_start_price:
          $ref: '#/components/schemas/NonNegativeAmount'
        auction_reserve_price:
          allOf:
            - $ref: '#/components/schemas/NonNegativeAmount'
          description: Requires `auction_start_price` when provided.
        marketplace_title:
          type: string
          maxLength: 255
        marketplace_description:
          type: string
          maxLength: 10000
        tags:
          type: array
          maxItems: 50
          items:
            type: string
            minLength: 1
            maxLength: 255
    Authenticator:
      type: string
      enum:
        - PSA
        - PSA/DNA
        - SGC
        - BGS
        - CGC
        - TAG
        - ISA
        - AGS
        - MBA
    ItemListingStatus:
      type: string
      enum:
        - LISTED
        - DELISTING
        - NOT_LISTED
      description: |
        Current public listing state. `DELISTING` means a delist request was accepted and is still
        being processed. A listing is treated as listed until every relevant listing has ended.
    ItemMarketplaceListing:
      type: object
      additionalProperties: false
      required:
        - marketplace
        - status
      properties:
        connection_id:
          description: Connection used to publish the listing. May be null for a legacy listing.
          type:
            - integer
            - 'null'
          format: int64
          minimum: 1
        marketplace:
          $ref: '#/components/schemas/MarketplaceType'
        status:
          type: string
          enum:
            - ACTIVE
            - SOLD
            - ORPHANED
            - PENDING
        listing_url:
          description: Marketplace listing URL. Mascot Network listings link to Mercury.
          type:
            - string
            - 'null'
          format: uri
    ItemAspect:
      type: object
      additionalProperties: false
      required:
        - aspect_id
        - aspect_name
        - values
      properties:
        aspect_id:
          type: integer
          format: int64
        aspect_name:
          type: string
        values:
          type: array
          items:
            type: string
    ItemInventoryCost:
      type: object
      additionalProperties: false
      properties:
        type:
          type:
            - string
            - 'null'
          enum:
            - CUSTOM
            - TAX
            - GRADING
            - SHIPPING
            - BUYERS_PREMIUM
            - null
        amount:
          type:
            - number
            - 'null'
        description:
          type:
            - string
            - 'null'
    ItemInventoryEntry:
      type: object
      additionalProperties: false
      properties:
        quantity:
          type:
            - integer
            - 'null'
        quantity_sold:
          type:
            - integer
            - 'null'
        quantity_reserved:
          type:
            - integer
            - 'null'
          format: int64
        unit_cost:
          type:
            - number
            - 'null'
        total_cost:
          type:
            - number
            - 'null'
          description: Unit cost plus extra inventory costs for this entry.
        purchase_date:
          type:
            - string
            - 'null'
          format: date-time
        expected_delivery_date:
          type:
            - string
            - 'null'
          format: date-time
        item_received_date:
          type:
            - string
            - 'null'
          format: date-time
        location:
          type:
            - string
            - 'null'
        purchased_from:
          type:
            - string
            - 'null'
        buyer:
          type:
            - string
            - 'null'
        costs:
          type:
            - array
            - 'null'
          items:
            $ref: '#/components/schemas/ItemInventoryCost'
    ItemRecentComp:
      type: object
      additionalProperties: false
      properties:
        date:
          type:
            - string
            - 'null'
          format: date-time
        price:
          type:
            - number
            - 'null'
        url:
          type:
            - string
            - 'null'
          format: uri
    ItemVcpPricing:
      type: object
      additionalProperties: false
      properties:
        match_status:
          type:
            - string
            - 'null'
        error_message:
          type:
            - string
            - 'null'
        grader:
          type:
            - string
            - 'null'
        grade:
          type:
            - string
            - 'null'
        last_price_realized:
          type:
            - number
            - 'null'
        high_price:
          type:
            - number
            - 'null'
        low_price:
          type:
            - number
            - 'null'
        average_price:
          type:
            - number
            - 'null'
        price_change:
          type:
            - number
            - 'null'
        last_auction_date:
          type:
            - string
            - 'null'
          format: date-time
        total_auctions:
          type:
            - integer
            - 'null'
        link:
          type:
            - string
            - 'null'
          format: uri
        date_modified:
          type:
            - string
            - 'null'
          format: date-time
        captured_at:
          type:
            - string
            - 'null'
          format: date-time
    ItemRepricingEvent:
      type: object
      additionalProperties: false
      properties:
        old_price:
          type:
            - number
            - 'null'
        new_price:
          type:
            - number
            - 'null'
        event_time:
          type:
            - string
            - 'null'
          format: date-time
    ItemAutoRepricing:
      type: object
      additionalProperties: false
      properties:
        start_price:
          type:
            - number
            - 'null'
        decrement:
          type:
            - number
            - 'null'
        floor_price:
          type:
            - number
            - 'null'
        start_date:
          type:
            - string
            - 'null'
          format: date-time
        frequency_minutes:
          type:
            - integer
            - 'null'
        duration_minutes:
          type:
            - integer
            - 'null'
        accept_offers_percentage:
          type:
            - number
            - 'null'
        next_repricing_at:
          type:
            - string
            - 'null'
          format: date-time
        expected_iterations:
          type:
            - integer
            - 'null'
        completed_iterations:
          type:
            - integer
            - 'null'
        status:
          type:
            - string
            - 'null'
          enum:
            - ACTIVE
            - FLOOR_REACHED
            - DISABLED
            - null
        last_event:
          oneOf:
            - $ref: '#/components/schemas/ItemRepricingEvent'
            - type: 'null'
    ItemMbaCertification:
      type: object
      additionalProperties: false
      properties:
        item_description:
          type:
            - string
            - 'null'
        third_party_grader:
          type:
            - string
            - 'null'
        third_party_serial_number:
          type:
            - string
            - 'null'
        status:
          type:
            - string
            - 'null'
        plaque_image:
          type:
            - string
            - 'null'
          format: uri
        certification_icon_image:
          type:
            - string
            - 'null'
          format: uri
        diamond_certification:
          type:
            - string
            - 'null'
        diamond_certification_front_image:
          type:
            - string
            - 'null'
          format: uri
        diamond_certification_back_image:
          type:
            - string
            - 'null'
          format: uri
        certification_candidacy:
          type:
            - string
            - 'null'
        third_party_grade:
          type:
            - number
            - 'null'
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
    ItemPagination:
      type: object
      additionalProperties: false
      required:
        - page
        - page_size
        - total_items
        - total_pages
      properties:
        page:
          type: integer
          minimum: 1
        page_size:
          type: integer
          minimum: 1
          maximum: 200
        total_items:
          type: integer
          format: int64
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    FindItemsResponse:
      type: object
      additionalProperties: false
      required:
        - items
        - pagination
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/Item'
        pagination:
          $ref: '#/components/schemas/ItemPagination'
    ItemListingConnectionRequest:
      type: object
      additionalProperties: false
      required:
        - connection_id
      properties:
        connection_id:
          description: Concrete connection ID returned by the connections endpoint.
          type: integer
          format: int64
          minimum: 1
        location_id:
          description: |
            Optional location returned for this connection. Supported by Shopify, Square, and Walmart.
            Do not provide it for Mascot Network or marketplaces without selectable locations.
          type:
            - string
            - 'null'
          maxLength: 255
    CreateItemListingRequest:
      type: object
      additionalProperties: false
      required:
        - connections
      properties:
        connections:
          type: array
          description: Concrete marketplace connections and optional per-connection locations.
          minItems: 1
          maxItems: 50
          uniqueItems: true
          items:
            $ref: '#/components/schemas/ItemListingConnectionRequest'
    CreateFeedItem:
      allOf:
        - $ref: '#/components/schemas/CreateItemRequest'
    UpdateFeedItem:
      allOf:
        - $ref: '#/components/schemas/ItemWritableFields'
        - type: object
          required:
            - item_id
          properties:
            item_id:
              type: string
              format: uuid
      unevaluatedProperties: false
    CreateItemFeedRequest:
      oneOf:
        - type: object
          additionalProperties: false
          required:
            - type
            - items
          properties:
            type:
              const: CREATE
            items:
              type: array
              minItems: 1
              maxItems: 1000
              items:
                $ref: '#/components/schemas/CreateFeedItem'
        - type: object
          additionalProperties: false
          required:
            - type
            - items
          properties:
            type:
              const: UPDATE
            items:
              type: array
              minItems: 1
              maxItems: 1000
              items:
                $ref: '#/components/schemas/UpdateFeedItem'
        - type: object
          additionalProperties: false
          required:
            - type
            - item_ids
          properties:
            type:
              type: string
              enum:
                - DELETE
                - LIST
                - DELIST
            item_ids:
              type: array
              minItems: 1
              maxItems: 1000
              uniqueItems: true
              items:
                type: string
                format: uuid
      description: |
        `CREATE` uses the same item request objects as the synchronous create endpoint. `UPDATE`
        uses the same merge-patch fields and requires `item_id` inside every item. `DELETE`, `LIST`,
        and `DELIST` use `item_ids`. The maximum feed size is configurable; the documented service
        limit is 1,000 tasks unless Mascot provides a different limit for the integration.
    ItemFeedType:
      type: string
      enum:
        - CREATE
        - UPDATE
        - DELETE
        - LIST
        - DELIST
    ItemFeedStatus:
      type: string
      enum:
        - QUEUED
        - IN_PROGRESS
        - FINISHED
    ItemFeedTaskStatus:
      type: string
      enum:
        - QUEUED
        - IN_PROGRESS
        - SUCCESS
        - ERROR
    ItemFeedTask:
      type: object
      additionalProperties: false
      required:
        - task_id
        - position
        - status
        - created_at
        - updated_at
      properties:
        task_id:
          type: string
          format: uuid
        position:
          type: integer
          minimum: 1
        item_id:
          type:
            - string
            - 'null'
          format: uuid
        external_id:
          type:
            - string
            - 'null'
          maxLength: 255
        status:
          $ref: '#/components/schemas/ItemFeedTaskStatus'
        error_message:
          type:
            - string
            - 'null'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TaskPagination:
      type: object
      additionalProperties: false
      required:
        - page
        - page_size
        - total_tasks
        - total_pages
      properties:
        page:
          type: integer
          minimum: 1
        page_size:
          type: integer
          minimum: 1
          maximum: 200
        total_tasks:
          type: integer
          format: int64
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    FindItemFeedTasksResponse:
      type: object
      additionalProperties: false
      required:
        - tasks
        - pagination
      properties:
        tasks:
          type: array
          items:
            $ref: '#/components/schemas/ItemFeedTask'
        pagination:
          $ref: '#/components/schemas/TaskPagination'
    Amount:
      type: number
      minimum: 0
      multipleOf: 0.01
    CurrencyCode:
      type: string
      pattern: ^[A-Z]{3}$
      examples:
        - USD
    Money:
      type: object
      additionalProperties: false
      required:
        - amount
        - currency
      properties:
        amount:
          $ref: '#/components/schemas/Amount'
        currency:
          $ref: '#/components/schemas/CurrencyCode'
    FindProductsResponse:
      type: object
      additionalProperties: false
      required:
        - products
        - next_cursor
        - has_more
      properties:
        products:
          type: array
          items:
            $ref: '#/components/schemas/Product'
        next_cursor:
          type:
            - string
            - 'null'
          description: Opaque cursor for the next page. Send it with exactly the same filters and sorting.
        has_more:
          type: boolean
    Address:
      type: object
      additionalProperties: false
      properties:
        name:
          type:
            - string
            - 'null'
        address_line_1:
          type:
            - string
            - 'null'
        address_line_2:
          type:
            - string
            - 'null'
        city:
          type:
            - string
            - 'null'
        state:
          type:
            - string
            - 'null'
        postal_code:
          type:
            - string
            - 'null'
        country_code:
          type:
            - string
            - 'null'
    FindPurchasesResponse:
      type: object
      additionalProperties: false
      required:
        - purchases
        - page
        - page_size
        - total_elements
        - total_pages
      properties:
        purchases:
          type: array
          items:
            $ref: '#/components/schemas/Purchase'
        page:
          type: integer
          minimum: 1
        page_size:
          type: integer
          minimum: 1
          maximum: 200
        total_elements:
          type: integer
          format: int64
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    CreatePurchaseRequest:
      type: object
      additionalProperties: false
      required:
        - external_id
        - product_id
      properties:
        external_id:
          type: string
          minLength: 1
          maxLength: 255
          description: Idempotency identifier assigned by the buyer integration.
        product_id:
          type: string
          format: uuid
    FindOffersResponse:
      type: object
      additionalProperties: false
      required:
        - offers
        - page
        - page_size
        - total_elements
        - total_pages
      properties:
        offers:
          type: array
          items:
            $ref: '#/components/schemas/Offer'
        page:
          type: integer
          minimum: 1
        page_size:
          type: integer
          minimum: 1
          maximum: 200
        total_elements:
          type: integer
          format: int64
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    CreateOfferRequest:
      type: object
      additionalProperties: false
      required:
        - external_id
        - product_id
        - amount
      properties:
        external_id:
          type: string
          minLength: 1
          maxLength: 255
          description: Idempotency identifier assigned by the buyer integration.
        product_id:
          type: string
          format: uuid
        amount:
          type: number
          minimum: 0.01
          multipleOf: 0.01
          description: Amount offered by the buyer.
    OfferActionRequest:
      type: object
      additionalProperties: false
      required:
        - external_id
      properties:
        external_id:
          type: string
          minLength: 1
          maxLength: 255
          description: Idempotency identifier for this accept or reject action.
    SalePayout:
      type: object
      additionalProperties: false
      required:
        - status
        - amount
        - currency
        - method
        - eligible_at
      properties:
        status:
          type: string
          enum:
            - PENDING
            - PROCESSING
            - PAID
            - FAILED
            - REVERSED
          description: |
            `PENDING` is waiting for payout eligibility, `PROCESSING` was submitted for payment,
            `PAID` completed, `FAILED` did not complete, and `REVERSED` was returned after completion.
        amount:
          type: number
          minimum: 0
          multipleOf: 0.01
          description: Seller-visible payout amount.
        currency:
          $ref: '#/components/schemas/CurrencyCode'
        method:
          type: string
          enum:
            - STRIPE
            - PAYPAL
        eligible_at:
          type: string
          format: date-time
          description: Date and time at which the sale becomes eligible for payout.
        paid_at:
          type:
            - string
            - 'null'
          format: date-time
    SaleShipment:
      type: object
      additionalProperties: false
      required:
        - package_id
      properties:
        package_id:
          type: string
          format: uuid
        tracking_number:
          type:
            - string
            - 'null'
        tracking_status:
          type:
            - string
            - 'null'
        tracking_url:
          type:
            - string
            - 'null'
          format: uri
        carrier:
          type:
            - string
            - 'null'
        service:
          type:
            - string
            - 'null'
        eta:
          type:
            - string
            - 'null'
        ship_to:
          oneOf:
            - $ref: '#/components/schemas/Address'
            - type: 'null'
    SalesPagination:
      type: object
      additionalProperties: false
      required:
        - page
        - page_size
        - total_sales
        - total_pages
      properties:
        page:
          type: integer
          minimum: 1
        page_size:
          type: integer
          minimum: 1
          maximum: 200
        total_sales:
          type: integer
          format: int64
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    FindSalesResponse:
      type: object
      additionalProperties: false
      required:
        - sales
        - pagination
      properties:
        sales:
          type: array
          items:
            $ref: '#/components/schemas/Sale'
        pagination:
          $ref: '#/components/schemas/SalesPagination'
    ShipFromLocation:
      type: object
      additionalProperties: false
      required:
        - id
        - name
        - default
        - address_line_1
        - city
        - state
        - postal_code
        - country_code
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        default:
          type: boolean
        address_line_1:
          type: string
        address_line_2:
          type:
            - string
            - 'null'
        city:
          type: string
        state:
          type: string
        postal_code:
          type: string
        country_code:
          type: string
    EligibleShippingSale:
      type: object
      additionalProperties: false
      required:
        - sale_id
        - order_id
        - package_id
        - order_date
        - ready_for_shipping
        - has_label
        - in_active_batch
      properties:
        sale_id:
          type: string
          format: uuid
        order_id:
          type:
            - string
            - 'null'
        package_id:
          type: string
          format: uuid
        order_date:
          type: string
          format: date-time
        ready_for_shipping:
          type: boolean
        unavailable_reason:
          type:
            - string
            - 'null'
        has_label:
          type: boolean
        label_ship_date:
          type:
            - string
            - 'null'
          format: date
        in_active_batch:
          type: boolean
        batch_id:
          type:
            - string
            - 'null'
          format: uuid
    FindEligibleSalesResponse:
      type: object
      additionalProperties: false
      required:
        - sales
        - pagination
      properties:
        sales:
          type: array
          items:
            $ref: '#/components/schemas/EligibleShippingSale'
        pagination:
          $ref: '#/components/schemas/SalesPagination'
    ShippingBatchStatus:
      type: string
      enum:
        - QUEUED
        - IN_PROGRESS
        - ACTION_REQUIRED
        - FINISHED
        - FAILED
    ShippingBatchStage:
      type:
        - string
        - 'null'
      enum:
        - VALIDATING
        - PURCHASING
        - MANIFESTING
        - null
    ShippingBatchCounts:
      type: object
      additionalProperties: false
      required:
        - total
        - pending
        - success
        - error
      properties:
        total:
          type: integer
          minimum: 0
        pending:
          type: integer
          minimum: 0
        success:
          type: integer
          minimum: 0
        error:
          type: integer
          minimum: 0
    ShippingBatchesPagination:
      type: object
      additionalProperties: false
      required:
        - page
        - page_size
        - total_batches
        - total_pages
      properties:
        page:
          type: integer
          minimum: 1
        page_size:
          type: integer
          minimum: 1
          maximum: 200
        total_batches:
          type: integer
          format: int64
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    FindShippingBatchesResponse:
      type: object
      additionalProperties: false
      required:
        - batches
        - pagination
      properties:
        batches:
          type: array
          items:
            $ref: '#/components/schemas/ShippingBatch'
        pagination:
          $ref: '#/components/schemas/ShippingBatchesPagination'
    CreateShippingBatchRequest:
      type: object
      additionalProperties: false
      required:
        - external_id
        - sale_ids
      properties:
        external_id:
          type: string
          minLength: 1
          maxLength: 255
          description: Idempotency identifier assigned by the integration.
        sale_ids:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items:
            type: string
            format: uuid
        ship_from_location_id:
          type: string
          format: uuid
          description: Uses the collector's default ship-from location when omitted.
        ship_date:
          type: string
          format: date
          description: Cannot be in the future. Uses the current date when omitted.
    ShippingBatchItem:
      type: object
      additionalProperties: false
      required:
        - id
        - sale_id
        - package_id
        - item_id
        - title
        - quantity
        - status
      properties:
        id:
          type: string
          format: uuid
        sale_id:
          type: string
          format: uuid
        package_id:
          type: string
          format: uuid
        item_id:
          type:
            - string
            - 'null'
          format: uuid
        title:
          type:
            - string
            - 'null'
        quantity:
          type:
            - integer
            - 'null'
        status:
          type: string
          enum:
            - PENDING
            - SUCCESS
            - ERROR
        tracking_number:
          type:
            - string
            - 'null'
        error_message:
          type:
            - string
            - 'null'
    ShippingDocumentType:
      type: string
      enum:
        - LABELS
        - PICK_LIST
        - MANIFEST
    ShippingBatchDetail:
      type: object
      additionalProperties: false
      required:
        - batch
        - items
        - available_documents
      properties:
        batch:
          $ref: '#/components/schemas/ShippingBatch'
        items:
          type: array
          items:
            $ref: '#/components/schemas/ShippingBatchItem'
        available_documents:
          type: array
          items:
            $ref: '#/components/schemas/ShippingDocumentType'
    RetryShippingBatchRequest:
      type: object
      additionalProperties: false
      required:
        - external_id
      properties:
        external_id:
          type: string
          minLength: 1
          maxLength: 255
          description: New idempotency identifier for the retry batch.
    ShippingDocument:
      type: object
      additionalProperties: false
      required:
        - type
        - url
      properties:
        type:
          $ref: '#/components/schemas/ShippingDocumentType'
        url:
          type: string
          format: uri
          description: Temporary URL for downloading the requested document.
    WebhookEventType:
      type: string
      enum:
        - item.created
        - item.updated
        - item.deleted
        - listing.status_changed
        - listing.failed
        - listing.delisting_failed
        - item_feed.finished
        - sale.created
        - sale.updated
        - shipment.tracking_updated
        - shipping_batch.finished
        - shipping_batch.action_required
        - shipping_batch.failed
        - purchase.created
        - purchase.updated
        - offer.created
        - offer.updated
    WebhookEndpointStatus:
      type: string
      enum:
        - ACTIVE
        - FAILING
        - DISABLED
    WebhookEndpointsPagination:
      type: object
      additionalProperties: false
      required:
        - page
        - page_size
        - total_subscriptions
        - total_pages
      properties:
        page:
          type: integer
          minimum: 1
        page_size:
          type: integer
          minimum: 1
          maximum: 200
        total_subscriptions:
          type: integer
          format: int64
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    FindWebhookEndpointsResponse:
      type: object
      additionalProperties: false
      required:
        - subscriptions
        - pagination
      properties:
        subscriptions:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEndpoint'
        pagination:
          $ref: '#/components/schemas/WebhookEndpointsPagination'
    WebhookEndpointRequest:
      type: object
      additionalProperties: false
      required:
        - url
        - event_types
      properties:
        url:
          type: string
          format: uri
          maxLength: 2048
          pattern: ^https://
          description: Public HTTPS destination. Redirects are not followed.
        event_types:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            $ref: '#/components/schemas/WebhookEventType'
          description: The API key must have the corresponding read scope for every selected event.
    DeletedItemEvent:
      type: object
      additionalProperties: false
      required:
        - item_id
      properties:
        item_id:
          type: string
          format: uuid
        external_id:
          type:
            - string
            - 'null'
    WebhookEnvelope:
      type: object
      additionalProperties: false
      required:
        - id
        - type
        - api_version
        - created_at
        - data
      properties:
        id:
          type: string
          description: Stable event identifier. Use it to deduplicate deliveries.
        type:
          $ref: '#/components/schemas/WebhookEventType'
        api_version:
          type: string
          const: v1
        created_at:
          type: string
          format: date-time
        data:
          oneOf:
            - $ref: '#/components/schemas/Item'
            - $ref: '#/components/schemas/DeletedItemEvent'
            - $ref: '#/components/schemas/ItemFeed'
            - $ref: '#/components/schemas/Sale'
            - $ref: '#/components/schemas/ShippingBatch'
            - $ref: '#/components/schemas/Purchase'
            - $ref: '#/components/schemas/Offer'
  responses:
    Unauthorized:
      description: Credentials are missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: invalid_token
            message: Invalid or missing public API credentials
    Forbidden:
      description: The token lacks the required scope or the request is not permitted.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: insufficient_scope
            message: The auth token does not have the required scope
    RateLimited:
      description: The API key exceeded its request rate limit.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: rate_limit_exceeded
            message: The API key has exceeded its request rate limit
    ServerError:
      description: Mascot could not complete the request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: server_error
            message: Unable to complete the request
    NotFound:
      description: The requested resource is unavailable to the authenticated collector.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: not_found
            message: Resource not found
    BadRequest:
      description: The request is malformed or violates a documented validation rule.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PaymentRequired:
      description: The saved payment method could not complete the purchase.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: The request conflicts with the current resource state or reuses an external ID differently.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  parameters:
    ItemId:
      name: itemId
      in: path
      required: true
      description: Public UUID returned by the item API.
      schema:
        type: string
        format: uuid
    FeedId:
      name: feedId
      in: path
      required: true
      description: Item feed identifier returned when the feed was submitted.
      schema:
        type: string
        format: uuid
    ProductId:
      name: productId
      in: path
      required: true
      description: Product identifier returned by product discovery.
      schema:
        type: string
        format: uuid
    OfferId:
      name: offerId
      in: path
      required: true
      description: Offer identifier returned by the offer API or an offer webhook.
      schema:
        type: string
        format: uuid
    BatchId:
      name: batchId
      in: path
      required: true
      description: Shipping batch identifier.
      schema:
        type: string
        format: uuid
    EndpointId:
      name: id
      in: path
      required: true
      description: Webhook endpoint identifier.
      schema:
        type: string
        format: uuid
