openapi: 3.1.0
info:
  title: Yiwu Source Agent Procurement API
  version: 0.1.0
  description: Structured supply and fulfillment API for buying agents.
servers:
  - url: https://buyingmesh.com
security:
  - bearerAuth: []
paths:
  /v1/merchant/applications:
    post:
      operationId: createMerchantApplication
      summary: Submit a merchant onboarding application
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [companyName, workEmail, category, catalogSize]
              properties:
                companyName: {type: string, maxLength: 160}
                workEmail: {type: string, format: email}
                category: {type: string, maxLength: 160}
                catalogSize: {type: string, maxLength: 80}
      responses:
        '201': {description: Application received}
        '422': {$ref: '#/components/responses/ValidationError'}
  /v1/merchant/products/import:
    post:
      operationId: importMerchantProducts
      summary: Import a merchant product sample for review
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [applicationId, products]
              properties:
                applicationId: {type: string, format: uuid}
                products:
                  type: array
                  maxItems: 500
                  items: {$ref: '#/components/schemas/MerchantProductImport'}
      responses:
        '201': {description: Import accepted for review}
        '404': {$ref: '#/components/responses/NotFound'}
        '422': {$ref: '#/components/responses/ValidationError'}
  /v1/admin/applications:
    get:
      operationId: listMerchantApplicationsForReview
      summary: List merchant applications and review counts
      security:
        - adminReviewKey: []
      parameters:
        - {name: status, in: query, schema: {type: string, enum: [received, under_review, approved, changes_requested, needs_info, rejected]}}
      responses:
        '200': {description: Review queue}
        '401': {$ref: '#/components/responses/Unauthorized'}
  /v1/admin/applications/{applicationId}:
    get:
      operationId: getMerchantApplicationForReview
      summary: Get an application and its submitted products
      security:
        - adminReviewKey: []
      parameters:
        - {name: applicationId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        '200': {description: Application detail}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '404': {$ref: '#/components/responses/NotFound'}
    post:
      operationId: reviewMerchantApplication
      summary: Approve, reject or request changes for an application
      security:
        - adminReviewKey: []
      parameters:
        - {name: applicationId, in: path, required: true, schema: {type: string, format: uuid}}
        - {name: X-Admin-Key, in: header, required: true, schema: {type: string}}
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: '#/components/schemas/ReviewAction'}}}
      responses:
        '200': {description: Review saved}
        '401': {$ref: '#/components/responses/Unauthorized'}
  /v1/admin/products:
    get:
      operationId: listMerchantProductsForReview
      summary: List submitted products for review
      security:
        - adminReviewKey: []
      parameters:
        - {name: status, in: query, schema: {type: string, enum: [pending_review, approved, changes_requested, needs_info, rejected]}}
      responses:
        '200': {description: Product review queue}
        '401': {$ref: '#/components/responses/Unauthorized'}
  /v1/admin/products/{productId}:
    post:
      operationId: reviewMerchantProduct
      summary: Approve or request changes for a submitted product
      security:
        - adminReviewKey: []
      parameters:
        - {name: productId, in: path, required: true, schema: {type: string, format: uuid}}
        - {name: X-Admin-Key, in: header, required: true, schema: {type: string}}
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: '#/components/schemas/ReviewAction'}}}
      responses:
        '200': {description: Review saved}
        '401': {$ref: '#/components/responses/Unauthorized'}
        '404': {$ref: '#/components/responses/NotFound'}
  /v1/products:
    get:
      operationId: searchProducts
      summary: Search structured products
      parameters:
        - {name: q, in: query, schema: {type: string}}
        - {name: category, in: query, schema: {type: string, example: stainless-fasteners}}
        - {name: supplierId, in: query, schema: {type: string}}
        - {name: minMoq, in: query, schema: {type: integer, minimum: 1}}
        - {name: maxPrice, in: query, schema: {type: number, format: double}}
        - {name: freshnessWithinMinutes, in: query, schema: {type: integer, default: 60}}
        - {name: cursor, in: query, schema: {type: string}}
        - {name: limit, in: query, schema: {type: integer, minimum: 1, maximum: 100, default: 20}}
      responses:
        '200':
          description: Product results
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ProductSearchResponse'}
        '402':
          $ref: '#/components/responses/PaymentRequired'
  /v1/products/{sku}:
    get:
      operationId: getProduct
      summary: Get a complete product JSON-LD document
      parameters:
        - {name: sku, in: path, required: true, schema: {type: string}}
      responses:
        '200':
          description: Product detail
          content:
            application/ld+json:
              schema: {$ref: '#/components/schemas/Product'}
        '404': {$ref: '#/components/responses/NotFound'}
        '402': {$ref: '#/components/responses/PaymentRequired'}
  /v1/merchant/products:
    get:
      operationId: listMerchantProducts
      summary: List merchant products
      parameters:
        - {name: X-Merchant-Key, in: header, required: true, schema: {type: string}}
      responses:
        '200': {description: Product list}
        '401': {$ref: '#/components/responses/Unauthorized'}
    post:
      operationId: createMerchantProduct
      summary: Create a merchant product
      parameters:
        - {name: X-Merchant-Key, in: header, required: true, schema: {type: string}}
      requestBody:
        required: true
        content: {application/json: {schema: {$ref: '#/components/schemas/Product'}}}
      responses:
        '201': {description: Product created}
        '401': {$ref: '#/components/responses/Unauthorized'}
  /v1/merchant/products/{sku}:
    patch:
      operationId: updateMerchantProduct
      summary: Update merchant price, inventory, lead time or qualifications
      parameters:
        - {name: sku, in: path, required: true, schema: {type: string}}
        - {name: X-Merchant-Key, in: header, required: true, schema: {type: string}}
      requestBody:
        required: true
        content: {application/json: {schema: {type: object}}}
      responses:
        '200': {description: Product updated}
        '401': {$ref: '#/components/responses/Unauthorized'}
  /v1/quotes:
    post:
      operationId: compareQuotes
      summary: Generate comparable quotes from SKU, quantity and destination
      parameters:
        - {$ref: '#/components/parameters/IdempotencyKey'}
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/QuoteRequest'}
      responses:
        '200':
          description: Quote result
          content:
            application/json:
              schema: {$ref: '#/components/schemas/QuoteResponse'}
        '409': {$ref: '#/components/responses/Conflict'}
  /v1/orders:
    post:
      operationId: createOrder
      summary: Create a B2B procurement order
      parameters:
        - {$ref: '#/components/parameters/IdempotencyKey'}
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CreateOrderRequest'}
      responses:
        '201':
          description: Order confirmed
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Order'}
        '422': {$ref: '#/components/responses/ValidationError'}
  /v1/orders/{orderId}:
    get:
      operationId: getOrder
      summary: Get order and latest fulfillment status
      parameters:
        - {name: orderId, in: path, required: true, schema: {type: string, format: uuid}}
      responses:
        '200':
          description: Order detail
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Order'}
        '404': {$ref: '#/components/responses/NotFound'}
  /v1/orders/{orderId}/tracking:
    post:
      operationId: attachTracking
      summary: Attach tracking and trigger a tracking sync
      parameters:
        - {name: orderId, in: path, required: true, schema: {type: string, format: uuid}}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [carrier, trackingNumber]
              properties:
                carrier: {type: string, enum: [kuaidi100, 17track, other]}
                trackingNumber: {type: string}
      responses:
        '202':
          description: Async sync accepted
          content:
            application/json:
              schema: {$ref: '#/components/schemas/Order'}
  /v1/orders/{orderId}/status:
    post:
      operationId: transitionOrder
      summary: Advance the order state machine
      parameters:
        - {name: orderId, in: path, required: true, schema: {type: string, format: uuid}}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [status]
              properties:
                status: {type: string, enum: [shipped, tracking_updated, eta_updated, delivered, accepted, released, disputed, cancelled]}
                data: {type: object}
      responses:
        '200':
          description: Status advanced
          content: {application/json: {schema: {$ref: '#/components/schemas/Order'}}}
        '409': {$ref: '#/components/responses/Conflict'}
  /v1/webhook-subscriptions:
    post:
      operationId: createWebhookSubscription
      summary: Register an order status webhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url: {type: string, format: uri}
                events:
                  type: array
                  items: {type: string, enum: [order.confirmed, order.shipped, order.tracking_updated, order.eta_updated, order.delivered, order.accepted, order.released, order.disputed]}
      responses:
        '201': {description: Webhook registered}
  /v1/webhook-deliveries:
    get:
      operationId: listWebhookDeliveries
      summary: List webhook delivery records
      responses:
        '200': {description: Webhook delivery records}
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
    adminReviewKey:
      type: apiKey
      in: header
      name: X-Admin-Key
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema: {type: string, minLength: 8, maxLength: 128}
  responses:
    PaymentRequired:
      description: An x402 payment is required for this query
      headers:
        PAYMENT-REQUIRED:
          schema: {type: string}
          description: Base64-encoded x402 payment requirement
      content:
        application/json:
          schema:
            type: object
            properties:
              code: {type: string, const: payment_required}
              amount: {type: string, example: '0.01'}
              currency: {type: string, example: USDC}
    NotFound:
      description: Resource not found
      content: {application/json: {schema: {$ref: '#/components/schemas/Error'}}}
    Conflict:
      description: Idempotency or inventory conflict
      content: {application/json: {schema: {$ref: '#/components/schemas/Error'}}}
    ValidationError:
      description: Validation failed
      content: {application/json: {schema: {$ref: '#/components/schemas/Error'}}}
    Unauthorized:
      description: Merchant authentication failed
      content: {application/json: {schema: {$ref: '#/components/schemas/Error'}}}
  schemas:
    ReviewAction:
      type: object
      required: [status]
      properties:
        status: {type: string, enum: [received, under_review, approved, changes_requested, needs_info, rejected, pending_review]}
        note: {type: string, maxLength: 1000}
    Product:
      type: object
      required: [sku, name, material, size, moq, minOrderQuantity, price, inventory, leadTimeDays, supplierQualifications, dataFreshness]
      properties:
        '@context': {type: string, const: 'https://schema.org'}
        '@type': {type: string, const: Product}
        sku: {type: string}
        gtin: {type: string}
        name: {type: string}
        material: {type: string}
        size: {type: string}
        moq: {type: integer, minimum: 1}
        minOrderQuantity: {type: integer, minimum: 1}
        price: {type: object, properties: {amount: {type: number}, currency: {type: string}}}
        inventory: {type: integer, minimum: 0}
        leadTimeDays: {type: integer, minimum: 0}
        supplierQualifications: {type: array, items: {type: string}}
        dataFreshness: {type: string, format: date-time}
    ProductSearchResponse:
      type: object
      required: [items, nextCursor, queryCost]
      properties:
        items: {type: array, items: {$ref: '#/components/schemas/Product'}}
        nextCursor: {type: [string, 'null']}
        queryCost: {type: object, properties: {amount: {type: string}, currency: {type: string}}}
    MerchantProductImport:
      type: object
      required: [sku, name, moq, price_amount, inventory, lead_time_days]
      properties:
        sku: {type: string}
        name: {type: string}
        material: {type: string}
        size: {type: string}
        gtin: {type: string}
        moq: {type: integer, minimum: 1}
        price_amount: {type: number, minimum: 0}
        price_currency: {type: string, default: CNY}
        inventory: {type: integer, minimum: 0}
        lead_time_days: {type: integer, minimum: 0}
        qualification_status: {type: string}
        source_ref: {type: string}
    QuoteRequest:
      type: object
      required: [items, destination]
      properties:
        items: {type: array, items: {type: object, required: [sku, quantity], properties: {sku: {type: string}, quantity: {type: integer, minimum: 1}}}}
        destination: {type: object, required: [country, postalCode], properties: {country: {type: string}, postalCode: {type: string}}}
    QuoteResponse:
      type: object
      properties:
        quoteId: {type: string}
        expiresAt: {type: string, format: date-time}
        options: {type: array, items: {type: object, properties: {supplierId: {type: string}, subtotal: {type: number}, shipping: {type: number}, total: {type: number}, leadTimeDays: {type: integer}}}}
    CreateOrderRequest:
      type: object
      required: [quoteId, buyer, shippingAddress, payment]
      properties:
        quoteId: {type: string}
        buyer: {type: object, required: [name, email], properties: {name: {type: string}, email: {type: string, format: email}}}
        shippingAddress: {type: object}
        payment: {type: object, properties: {method: {type: string, enum: [usdc_escrow, fiat_manual]}, chain: {type: string}}}
    Order:
      type: object
      required: [id, status, events, payment]
      properties:
        id: {type: string, format: uuid}
        status: {type: string, enum: [confirmed, shipped, tracking_updated, eta_updated, delivered, accepted, released, disputed, cancelled]}
        tracking: {type: [object, 'null']}
        eta: {type: [string, 'null'], format: date-time}
        events: {type: array, items: {type: object, properties: {type: {type: string}, occurredAt: {type: string, format: date-time}}}}
        payment: {type: object, properties: {status: {type: string}, gross: {type: number}, platformFee: {type: number}, sellerNet: {type: number}, currency: {type: string}}}
    Error:
      type: object
      required: [code, message]
      properties:
        code: {type: string}
        message: {type: string}
        requestId: {type: string}



