basePath: /
definitions:
  partnerapi.BuildingConfiguration:
    properties:
      model:
        type: string
      roofColor:
        type: string
      roofMaterial:
        type: string
      siding:
        type: string
      sidingColor:
        type: string
      trimColor:
        type: string
    type: object
  partnerapi.ConfiguratorSessionCreateRequest:
    properties:
      customerId:
        description: |-
          CustomerID optionally attaches an existing customer (create one first
          via POST /partner/v1/customers). Ids from another company 404.
        type: string
      locationId:
        type: string
      quoteId:
        description: QuoteID reopens an existing quote's saved design.
        type: string
      returnUrl:
        description: |-
          ReturnURL must match the company's allowlisted origins
          (Settings > Developer API). Passed through to Structura so the shopper
          can be sent back to the partner's site after saving.
        type: string
      serialNumber:
        type: string
      ttlSeconds:
        type: integer
      workOrderId:
        description: WorkOrderID / SerialNumber launch an in-stock building's design.
        type: string
    type: object
  partnerapi.ConfiguratorSessionResponse:
    properties:
      customerId:
        type: string
      expiresAt:
        type: string
      launchUrl:
        description: |-
          LaunchURL is the single-use link to hand to the shopper. Opening it
          consumes the token and redirects into the configurator.
        type: string
      quoteId:
        type: string
      sessionId:
        type: string
    type: object
  partnerapi.ContractSummary:
    properties:
      contractNumber:
        type: string
      contractVersion:
        description: short code allocated per signature request
        type: string
      customerSigned:
        type: boolean
      customerSignedAt:
        type: string
      orderId:
        type: string
      salespersonSigned:
        type: boolean
      salespersonSignedAt:
        type: string
      signedPdfDocumentId:
        description: |-
          SignedPdfDocumentId references the stored contract PDF, usable against
          GET /partner/v1/documents/{id}/download.
        type: string
      status:
        description: 'Status: draft | out_for_signature | partially_signed | completed'
        type: string
    type: object
  partnerapi.CustomerCreateRequest:
    properties:
      address:
        type: string
      city:
        type: string
      code:
        type: string
      contactName:
        type: string
      contactPerson:
        type: string
      email:
        type: string
      externalReferences:
        additionalProperties:
          type: string
        type: object
      name:
        type: string
      phone:
        type: string
      state:
        type: string
      zipCode:
        type: string
    type: object
  partnerapi.CustomerItem:
    properties:
      active:
        type: boolean
      address:
        type: string
      city:
        type: string
      code:
        type: string
      contactName:
        type: string
      contactPerson:
        type: string
      createdAt:
        type: string
      email:
        type: string
      externalReferences:
        additionalProperties:
          type: string
        type: object
      id:
        type: string
      merged:
        description: |-
          Merge lineage: a merged duplicate keeps resolving by id but carries
          merged=true and points at the surviving record.
        type: boolean
      mergedAt:
        type: string
      mergedInto:
        type: string
      name:
        type: string
      phone:
        type: string
      state:
        type: string
      updatedAt:
        type: string
      version:
        type: integer
      zipCode:
        type: string
    type: object
  partnerapi.CustomerListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.CustomerItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.CustomerMergeRequest:
    properties:
      survivorId:
        description: SurvivorID is the customer that absorbs the duplicate in the
          path.
        type: string
    type: object
  partnerapi.CustomerMergeResponse:
    properties:
      mergedCustomerId:
        description: |-
          MergedCustomerId is the loser's id — it now resolves with merged=true
          and mergedInto pointing at the survivor.
        type: string
      relinkedSalesEntities:
        description: |-
          RelinkedSalesEntities counts the leads/quotes/orders moved to the
          survivor.
        type: integer
      survivor:
        $ref: '#/definitions/partnerapi.CustomerItem'
    type: object
  partnerapi.CustomerPatchRequest:
    properties:
      active:
        type: boolean
      address:
        type: string
      city:
        type: string
      code:
        type: string
      contactName:
        type: string
      contactPerson:
        type: string
      email:
        type: string
      externalReferences:
        additionalProperties:
          type: string
        type: object
      name:
        type: string
      phone:
        type: string
      state:
        type: string
      zipCode:
        type: string
    type: object
  partnerapi.DocumentDownloadResponse:
    properties:
      downloadUrl:
        type: string
      expiresAt:
        type: string
      fileName:
        type: string
    type: object
  partnerapi.DocumentItem:
    properties:
      createdAt:
        type: string
      fileName:
        type: string
      id:
        type: string
      mimeType:
        type: string
      name:
        type: string
      sizeBytes:
        type: integer
      type:
        description: stored category, e.g. Contract
        type: string
    type: object
  partnerapi.DocumentListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.DocumentItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.DomainItem:
    properties:
      apexDomain:
        type: string
      integrationId:
        description: IntegrationID is the DNS integration record the subdomain belongs
          to.
        type: string
      locations:
        items:
          $ref: '#/definitions/partnerapi.DomainLocation'
        type: array
      subdomain:
        type: string
      verified:
        description: |-
          Verified reports whether the subdomain's DNS records have been
          verified (per-subdomain check, falling back to the integration flag).
        type: boolean
    type: object
  partnerapi.DomainListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.DomainItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.DomainLocation:
    properties:
      code:
        type: string
      defaultForStore:
        description: |-
          DefaultForStore marks this domain as the location's default storefront
          domain. A location has at most one default domain across all domains.
        type: boolean
      hidePrices:
        description: HidePrices hides pricing for this location on this domain's storefront.
        type: boolean
      locationId:
        type: string
      name:
        type: string
      products:
        items:
          $ref: '#/definitions/partnerapi.DomainProduct'
        type: array
    type: object
  partnerapi.DomainProduct:
    properties:
      excludedUpgrades:
        description: ExcludedUpgrades are upgrade ids hidden for this product on this
          domain.
        items:
          type: string
        type: array
      productId:
        type: string
      sizes:
        description: Sizes are the size ids enabled for this product on this domain.
        items:
          type: string
        type: array
    type: object
  partnerapi.ErrorResponse:
    properties:
      error:
        type: string
      error_description:
        type: string
    type: object
  partnerapi.EventItem:
    properties:
      data:
        additionalProperties: {}
        type: object
      externalReferences:
        additionalProperties:
          type: string
        type: object
      id:
        type: string
      occurredAt:
        type: string
      resourceId:
        type: string
      resourceType:
        type: string
      resourceUrl:
        type: string
      resourceVersion:
        type: integer
      type:
        type: string
    type: object
  partnerapi.EventListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.EventItem'
        type: array
      hasMore:
        type: boolean
      nextCursor:
        type: string
    type: object
  partnerapi.EventRedeliverResponse:
    properties:
      enqueued:
        type: integer
      eventId:
        type: string
    type: object
  partnerapi.LeadCreateCustomer:
    properties:
      email:
        type: string
      name:
        type: string
      phone:
        type: string
    type: object
  partnerapi.LeadCreateRequest:
    properties:
      customer:
        $ref: '#/definitions/partnerapi.LeadCreateCustomer'
      externalReferences:
        additionalProperties:
          type: string
        type: object
      locationId:
        type: string
      salespersonEmail:
        type: string
      salespersonName:
        type: string
    type: object
  partnerapi.LeadItem:
    properties:
      createdAt:
        type: string
      customer:
        $ref: '#/definitions/partnerapi.PartnerCustomer'
      externalReferences:
        additionalProperties:
          type: string
        type: object
      id:
        type: string
      location:
        $ref: '#/definitions/partnerapi.PartnerLocation'
      orderNumber:
        type: integer
      salesperson:
        $ref: '#/definitions/partnerapi.PartnerSalesperson'
      status:
        type: string
      statusChangedAt:
        type: string
      updatedAt:
        type: string
      version:
        type: integer
    type: object
  partnerapi.LeadListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.LeadItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.LeadPatchRequest:
    properties:
      externalReferences:
        additionalProperties:
          type: string
        type: object
      salesLocation:
        type: string
      salespersonEmail:
        type: string
      salespersonName:
        type: string
    type: object
  partnerapi.LineItem:
    properties:
      amount:
        type: number
      category:
        type: string
      colorName:
        type: string
      id:
        type: string
      isStandardFeature:
        type: boolean
      name:
        type: string
      productId:
        type: string
      quantity:
        type: number
      side:
        type: string
      status:
        description: included | added | removed
        type: string
    type: object
  partnerapi.LineItemCreateRequest:
    properties:
      lineKey:
        description: |-
          LineKey makes the add idempotent: re-sending the same key returns the
          existing line (200) instead of duplicating it. Generated when omitted.
        type: string
      price:
        type: number
      productId:
        type: string
      quantity:
        description: default 1
        type: number
    type: object
  partnerapi.LineItemCreateResponse:
    properties:
      created:
        description: Created is false when the lineKey matched an existing line.
        type: boolean
      lineId:
        type: string
      price:
        type: number
      productId:
        type: string
      quantity:
        type: number
    type: object
  partnerapi.LineItemTotals:
    properties:
      added:
        type: integer
      included:
        type: integer
      removed:
        type: integer
    type: object
  partnerapi.LineItemsResponse:
    properties:
      configuration:
        $ref: '#/definitions/partnerapi.BuildingConfiguration'
      data:
        items:
          $ref: '#/definitions/partnerapi.LineItem'
        type: array
      totals:
        $ref: '#/definitions/partnerapi.LineItemTotals'
    type: object
  partnerapi.LocationCreateRequest:
    properties:
      active:
        type: boolean
      address:
        type: string
      city:
        type: string
      code:
        type: string
      contactEmail:
        type: string
      contactPerson:
        type: string
      latitude:
        type: number
      longitude:
        type: number
      name:
        type: string
      phone:
        type: string
      plant:
        type: boolean
      salesLot:
        type: boolean
      slug:
        type: string
      state:
        type: string
      zipCode:
        type: string
    type: object
  partnerapi.LocationItem:
    properties:
      active:
        type: boolean
      address:
        type: string
      city:
        type: string
      code:
        type: string
      contactEmail:
        type: string
      contactPerson:
        type: string
      createdAt:
        type: string
      id:
        type: string
      latitude:
        type: number
      longitude:
        type: number
      name:
        type: string
      phone:
        type: string
      plant:
        type: boolean
      region:
        description: |-
          Region is a dealer-defined grouping label (e.g. "Southeast"). Quotes
          and orders derive their region through their sales location.
        type: string
      salesLot:
        type: boolean
      slug:
        type: string
      state:
        type: string
      storeHours:
        additionalProperties:
          $ref: '#/definitions/partnerapi.StoreHoursDay'
        description: |-
          StoreHours is the weekly open/close schedule keyed by day
          ("mon".."sun"). Omitted when the location has no schedule configured.
        type: object
      timezone:
        description: |-
          Timezone is the company-level IANA timezone (locations don't carry
          their own yet). Store hours are interpreted in this zone.
        type: string
      updatedAt:
        type: string
      zipCode:
        type: string
    type: object
  partnerapi.LocationListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.LocationItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.LocationPatchRequest:
    properties:
      active:
        type: boolean
      address:
        type: string
      city:
        type: string
      code:
        type: string
      contactEmail:
        type: string
      contactPerson:
        type: string
      latitude:
        type: number
      longitude:
        type: number
      name:
        type: string
      phone:
        type: string
      plant:
        type: boolean
      salesLot:
        type: boolean
      slug:
        type: string
      state:
        type: string
      zipCode:
        type: string
    type: object
  partnerapi.LotStockAttributes:
    properties:
      roofColor:
        type: string
      roofMaterial:
        type: string
      siding:
        type: string
      sidingColor:
        type: string
      trimColor:
        type: string
    type: object
  partnerapi.LotStockItem:
    properties:
      attributes:
        allOf:
        - $ref: '#/definitions/partnerapi.LotStockAttributes'
        description: |-
          Attributes is the building's exterior configuration (siding, colors,
          roof) — the same values the work-order detail page shows. Omitted when
          the unit has no configurator.
      basePrice:
        type: number
      id:
        type: string
      images:
        items:
          type: string
        type: array
      locationId:
        type: string
      locationName:
        type: string
      locationSlug:
        type: string
      price:
        type: number
      purchaseType:
        type: string
      serialNumber:
        type: string
      sold:
        type: boolean
      title:
        type: string
      workOrderId:
        type: string
    type: object
  partnerapi.LotStockResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.LotStockItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.MeRateLimit:
    properties:
      burst:
        type: integer
      requestsPerSecond:
        type: integer
    type: object
  partnerapi.MeResponse:
    properties:
      companyId:
        type: string
      companyName:
        type: string
      credentialType:
        description: CredentialType is "api_key" or "oauth_client".
        type: string
      rateLimit:
        $ref: '#/definitions/partnerapi.MeRateLimit'
      scopes:
        items:
          type: string
        type: array
    type: object
  partnerapi.OrderCreateLineItem:
    properties:
      lineKey:
        description: |-
          LineKey is an optional stable per-line identifier; generated when
          omitted. Shows up as the line id on GET line-items and is the handle
          for DELETE line-items.
        type: string
      price:
        type: number
      productId:
        type: string
      quantity:
        description: default 1
        type: number
    type: object
  partnerapi.OrderCreateRequest:
    type: object
  partnerapi.OrderItem:
    properties:
      createdAt:
        type: string
      customer:
        $ref: '#/definitions/partnerapi.PartnerCustomer'
      deliveredAt:
        type: string
      deposits:
        $ref: '#/definitions/partnerapi.PartnerDeposits'
      expectedDeliveryDate:
        type: string
      externalReferences:
        additionalProperties:
          type: string
        type: object
      id:
        type: string
      location:
        $ref: '#/definitions/partnerapi.PartnerLocation'
      orderNumber:
        type: integer
      pricing:
        $ref: '#/definitions/partnerapi.PartnerPricing'
      rto:
        $ref: '#/definitions/partnerapi.PartnerRTO'
      salesperson:
        $ref: '#/definitions/partnerapi.PartnerSalesperson'
      serialNumber:
        type: string
      sourceQuoteId:
        type: string
      sourceQuoteNumber:
        type: integer
      status:
        type: string
      statusChangedAt:
        type: string
      updatedAt:
        type: string
      version:
        type: integer
      workOrderId:
        type: string
    type: object
  partnerapi.OrderListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.OrderItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.PartnerCustomer:
    properties:
      email:
        type: string
      name:
        type: string
      phone:
        type: string
    type: object
  partnerapi.PartnerDelivery:
    properties:
      deliveredAt:
        type: string
      driverName:
        type: string
      runNumber:
        type: integer
      runStatus:
        description: Open | Scheduled | En Route | Delivered
        type: string
      scheduledDate:
        description: per-stop delivery date on the run assignment
        type: string
    type: object
  partnerapi.PartnerDeposits:
    properties:
      balance:
        type: number
      downPayment:
        type: number
      securityDeposit:
        type: number
      totalDueToday:
        type: number
      totalPaid:
        type: number
    type: object
  partnerapi.PartnerLocation:
    properties:
      id:
        type: string
      name:
        type: string
      slug:
        type: string
    type: object
  partnerapi.PartnerPricing:
    properties:
      changeOrderFee:
        type: number
      monthlyPayment:
        type: number
      paymentType:
        description: rto | cash
        type: string
      subtotal:
        type: number
      total:
        type: number
    type: object
  partnerapi.PartnerRTO:
    properties:
      balance:
        type: number
      damageWaiver:
        type: number
      downPayment:
        type: number
      monthlyPayment:
        type: number
      providerName:
        type: string
      rent:
        type: number
      securityDeposit:
        type: number
      termMonths:
        type: integer
      totalDueToday:
        type: number
    type: object
  partnerapi.PartnerSalesperson:
    properties:
      email:
        type: string
      id:
        type: string
      name:
        type: string
    type: object
  partnerapi.PartnerStatusRequest:
    properties:
      actionDescription:
        type: string
      status:
        type: string
    type: object
  partnerapi.PaymentCreateRequest:
    properties:
      amount:
        type: number
      bankName:
        description: BankName applies to method "financed" only.
        type: string
      checkNumber:
        description: CheckNumber applies to method "check" only.
        type: string
      description:
        description: Description shows on the payment record (defaults to "<Method>
          payment").
        type: string
      method:
        description: 'Method is one of: cash, check, financed, manual.'
        type: string
    type: object
  partnerapi.PaymentItem:
    properties:
      amount:
        type: number
      createdAt:
        type: string
      customerId:
        type: string
      description:
        type: string
      id:
        type: string
      method:
        description: card | ach | cash | check | financed | manual
        type: string
      orderId:
        type: string
      providerReference:
        description: |-
          ProviderReference is the Stripe checkout-session or payment-intent id
          for card/ACH payments; empty for manual records.
        type: string
      refundedAmount:
        type: number
      refundedAt:
        type: string
      status:
        description: Open | pending | processing | paid | payment failed | Refunded
          | Canceled
        type: string
    type: object
  partnerapi.PaymentLinkCreateRequest:
    properties:
      amount:
        type: number
      currency:
        description: Currency defaults to "usd".
        type: string
      email:
        description: Email overrides the order's customer email as the link recipient.
        type: string
      name:
        description: 'Name labels the Stripe Checkout line item (defaults to "Order
          #<n>").'
        type: string
      sendEmail:
        description: SendEmail controls the customer payment-link email (default true).
        type: boolean
    type: object
  partnerapi.PaymentLinkResponse:
    properties:
      emailSent:
        description: EmailSent reports whether the payment-link email was queued.
        type: boolean
      expiresAt:
        type: string
      sessionId:
        type: string
      url:
        type: string
    type: object
  partnerapi.PaymentListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.PaymentItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.ProductCreateRequest:
    properties:
      active:
        type: boolean
      description:
        type: string
      length:
        type: number
      lineId:
        description: |-
          LineID links the new product under an existing product line (parent
          product) via the finished-good relationship.
        type: string
      name:
        type: string
      price:
        type: number
      sku:
        type: string
      width:
        type: number
    type: object
  partnerapi.ProductItem:
    properties:
      active:
        type: boolean
      createdAt:
        type: string
      description:
        type: string
      id:
        type: string
      images:
        description: |-
          Images are ready-to-use public URLs from the product's gallery,
          falling back to the legacy image slots on the product record.
        items:
          type: string
        type: array
      length:
        type: number
      name:
        type: string
      price:
        type: number
      sku:
        type: string
      updatedAt:
        type: string
      width:
        type: number
    type: object
  partnerapi.ProductListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.ProductItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.ProductPatchRequest:
    properties:
      active:
        type: boolean
      description:
        type: string
      length:
        type: number
      name:
        type: string
      price:
        type: number
      sku:
        type: string
      width:
        type: number
    type: object
  partnerapi.ProductSizeCreateRequest:
    properties:
      active:
        type: boolean
      length:
        type: number
      name:
        description: Name defaults to "<width>x<length>" when omitted.
        type: string
      price:
        type: number
      sku:
        type: string
      width:
        type: number
    type: object
  partnerapi.ProductSizeItem:
    properties:
      active:
        type: boolean
      id:
        type: string
      length:
        type: number
      name:
        type: string
      price:
        type: number
      productId:
        type: string
      sku:
        type: string
      width:
        type: number
    type: object
  partnerapi.QuoteConvertRequest:
    properties:
      salespersonEmail:
        type: string
      salespersonName:
        description: Optional salesperson overrides; fall back to whatever the quote
          has.
        type: string
    type: object
  partnerapi.QuoteCreateCustomer:
    properties:
      email:
        type: string
      name:
        type: string
      phone:
        type: string
    type: object
  partnerapi.QuoteCreateDeliveryAddress:
    properties:
      address:
        type: string
      city:
        type: string
      latitude:
        type: number
      longitude:
        type: number
      state:
        type: string
      zipCode:
        type: string
    type: object
  partnerapi.QuoteCreateRequest:
    properties:
      customer:
        $ref: '#/definitions/partnerapi.QuoteCreateCustomer'
      deliveryAddress:
        $ref: '#/definitions/partnerapi.QuoteCreateDeliveryAddress'
      externalReferences:
        additionalProperties:
          type: string
        type: object
      locationId:
        type: string
      note:
        type: string
      price:
        type: number
      purchaseType:
        type: string
      serialNumber:
        type: string
      workOrderId:
        type: string
    type: object
  partnerapi.QuoteItem:
    properties:
      converted:
        type: boolean
      convertedOrderId:
        type: string
      convertedOrderNumber:
        type: integer
      createdAt:
        type: string
      customer:
        $ref: '#/definitions/partnerapi.PartnerCustomer'
      externalReferences:
        additionalProperties:
          type: string
        type: object
      id:
        type: string
      location:
        $ref: '#/definitions/partnerapi.PartnerLocation'
      orderNumber:
        type: integer
      pricing:
        $ref: '#/definitions/partnerapi.PartnerPricing'
      rto:
        $ref: '#/definitions/partnerapi.PartnerRTO'
      salesperson:
        $ref: '#/definitions/partnerapi.PartnerSalesperson'
      serialNumber:
        type: string
      status:
        type: string
      statusChangedAt:
        type: string
      updatedAt:
        type: string
      validUntil:
        description: |-
          ValidUntil: when this date passes while the quote is still Open/Active,
          a quote.expired event is emitted (daily sweep). Settable via PATCH.
        type: string
      version:
        type: integer
      workOrderId:
        type: string
    type: object
  partnerapi.QuoteListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.QuoteItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.RoleItem:
    properties:
      description:
        type: string
      id:
        type: string
      isSystem:
        type: boolean
      key:
        type: string
      name:
        type: string
    type: object
  partnerapi.RolesResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.RoleItem'
        type: array
    type: object
  partnerapi.SalesOrderPatchRequest:
    properties:
      customerEmail:
        type: string
      customerName:
        type: string
      customerPhone:
        type: string
      deliveryAddress:
        type: string
      deliveryCity:
        type: string
      deliveryState:
        type: string
      deliveryZipCode:
        type: string
      externalReferences:
        additionalProperties:
          type: string
        type: object
      salesLocation:
        type: string
      salespersonEmail:
        type: string
      salespersonName:
        type: string
    type: object
  partnerapi.StatusChangeActor:
    properties:
      email:
        type: string
      name:
        type: string
    type: object
  partnerapi.StatusChangeItem:
    properties:
      actor:
        $ref: '#/definitions/partnerapi.StatusChangeActor'
      changedAt:
        type: string
      description:
        type: string
      previousStatus:
        type: string
      status:
        type: string
    type: object
  partnerapi.StatusHistoryResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.StatusChangeItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.StockTemplateItem:
    properties:
      basePrice:
        description: |-
          Prices mirror the public gallery: base price, upgrades total, and the
          total of removed standard features.
        type: number
      configuratorId:
        description: |-
          ConfiguratorID is the template's 3D configuration; StructuraURL
          deep-links the interactive 3D view of it.
        type: string
      description:
        type: string
      descriptionHtml:
        type: string
      id:
        type: string
      images:
        items:
          type: string
        type: array
      productId:
        type: string
      productName:
        type: string
      removedPrice:
        type: number
      sizeId:
        type: string
      structuraUrl:
        type: string
      tags:
        description: Tags are the template's public tags (internal tags are never
          exposed).
        items:
          type: string
        type: array
      templateName:
        type: string
      upgradesPrice:
        type: number
      workOrderNumber:
        type: string
    type: object
  partnerapi.StockTemplateListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.StockTemplateItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.StoreHoursDay:
    properties:
      enabled:
        type: boolean
      from:
        type: string
      to:
        type: string
    type: object
  partnerapi.TokenResponse:
    properties:
      access_token:
        type: string
      expires_in:
        type: integer
      scope:
        type: string
      token_type:
        type: string
    type: object
  partnerapi.UserCreateRequest:
    properties:
      allLocations:
        type: boolean
      email:
        type: string
      firstName:
        type: string
      invite:
        description: Invite controls the invitation email (default true).
        type: boolean
      lastName:
        type: string
      locationIds:
        description: |-
          LocationIDs restricts the user to specific locations; AllLocations
          grants the unrestricted assignment instead. Mutually exclusive.
        items:
          type: string
        type: array
      phone:
        type: string
      roleId:
        description: RoleID references a company RBAC role — discover ids via GET
          /partner/v1/roles.
        type: string
    type: object
  partnerapi.UserItem:
    properties:
      active:
        type: boolean
      allLocations:
        type: boolean
      createdAt:
        type: string
      email:
        type: string
      id:
        type: string
      inLeadRoutingPool:
        type: boolean
      locationIds:
        items:
          type: string
        type: array
      name:
        type: string
      phone:
        type: string
    type: object
  partnerapi.UserListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.UserItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.UserPatchRequest:
    properties:
      active:
        description: |-
          Active enables/disables the user's membership in this company. The
          company owner cannot be disabled.
        type: boolean
      allLocations:
        type: boolean
      email:
        type: string
      firstName:
        type: string
      lastName:
        type: string
      locationIds:
        description: LocationIDs, when present, REPLACES the user's location assignments.
        items:
          type: string
        type: array
      phone:
        type: string
      roleId:
        type: string
    type: object
  partnerapi.WebhookDeliveryItem:
    properties:
      attempt:
        type: integer
      deliveredAt:
        type: string
      durationMs:
        type: integer
      error:
        type: string
      eventId:
        type: string
      eventType:
        type: string
      id:
        type: string
      ok:
        type: boolean
      statusCode:
        type: integer
      subscriptionId:
        type: string
      url:
        type: string
    type: object
  partnerapi.WebhookDeliveryListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.WebhookDeliveryItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.WorkOrderCreateRequest:
    properties:
      deliveryType:
        description: delivery-from-factory | built-on-site | delivered-from-dealer
          | delivered-from-lot-stock
        type: string
      externalReferences:
        additionalProperties:
          type: string
        type: object
      locationId:
        description: 'required: building location id or slug'
        type: string
      promisedDate:
        type: string
      purchaseType:
        description: new-build | existing-physical-inventory | general | stock
        type: string
      serialNumber:
        description: optional; uniqueness enforced (409)
        type: string
      sizeId:
        description: optional product size to attach
        type: string
      title:
        type: string
      workOrderType:
        description: made-to-order | lot-stock | rental-return | immediate-sale |
          template
        type: string
    type: object
  partnerapi.WorkOrderItem:
    properties:
      basePrice:
        type: number
      createdAt:
        type: string
      delivery:
        allOf:
        - $ref: '#/definitions/partnerapi.PartnerDelivery'
        description: detail endpoint only
      externalReferences:
        additionalProperties:
          type: string
        type: object
      id:
        type: string
      location:
        $ref: '#/definitions/partnerapi.PartnerLocation'
      orderId:
        description: linked sales order
        type: string
      orderNumber:
        description: linked sales order number
        type: integer
      promisedDate:
        type: string
      serialNumber:
        type: string
      status:
        type: string
      statusChangedAt:
        type: string
      title:
        type: string
      updatedAt:
        type: string
      version:
        type: integer
      workOrderNumber:
        type: integer
    type: object
  partnerapi.WorkOrderListResponse:
    properties:
      data:
        items:
          $ref: '#/definitions/partnerapi.WorkOrderItem'
        type: array
      limit:
        type: integer
      page:
        type: integer
      total:
        type: integer
    type: object
  partnerapi.WorkOrderPatchRequest:
    properties:
      buildingLocation:
        type: string
      description:
        type: string
      externalReferences:
        additionalProperties:
          type: string
        type: object
      promisedDate:
        type: string
      title:
        type: string
    type: object
info:
  contact: {}
  description: Public, company-scoped API for third-party integrations. Authenticate
    with either a company API key or an OAuth2 client_credentials access token — both
    grant the same scoped access. See docs/PARTNER_API.md for the full guide.
  title: ShedCloud Partner API
  version: "1.0"
paths:
  /oauth/token:
    post:
      consumes:
      - application/x-www-form-urlencoded
      description: Exchanges OAuth2 client credentials for a short-lived bearer access
        token (client_credentials grant, RFC 6749 §4.4). Send client_id/client_secret
        via HTTP Basic auth or as form fields.
      parameters:
      - description: Must be 'client_credentials'
        in: formData
        name: grant_type
        required: true
        type: string
      - description: OAuth client id (omit if using HTTP Basic auth)
        in: formData
        name: client_id
        type: string
      - description: OAuth client secret (omit if using HTTP Basic auth)
        in: formData
        name: client_secret
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.TokenResponse'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - BasicAuth: []
      summary: Issue an access token
      tags:
      - partner-oauth
  /partner/v1/configurator-sessions:
    post:
      consumes:
      - application/json
      description: Mints a single-use launch URL for the Structura 3D configurator.
        Optionally attach a customer, reopen a quote's design (quoteId), or open an
        in-stock building (workOrderId or serialNumber). returnUrl must match an allowlisted
        origin configured in Settings > Developer API. Supports the Idempotency-Key
        header.
      parameters:
      - description: Session parameters
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.ConfiguratorSessionCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.ConfiguratorSessionResponse'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a configurator session
      tags:
      - partner-configurator-sessions
  /partner/v1/customers:
    get:
      description: Returns the authenticated company's customers with filtering, sorting,
        and pagination.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive substring match on name, email, or phone
        in: query
        name: search
        type: string
      - description: Filter by email (substring)
        in: query
        name: email
        type: string
      - description: Filter by phone (substring)
        in: query
        name: phone
        type: string
      - description: Include customers that were merged into another record (excluded
          by default)
        in: query
        name: includeMerged
        type: boolean
      - description: Filter by external reference, key:value exact match
        in: query
        name: externalRef
        type: string
      - description: Created on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdFrom
        type: string
      - description: Created on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdTo
        type: string
      - description: Updated on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedFrom
        type: string
      - description: Updated on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedTo
        type: string
      - default: createdAt
        description: 'Sort field: name | email | createdAt | updatedAt'
        in: query
        name: sort
        type: string
      - default: desc
        description: 'Sort direction: asc | desc'
        in: query
        name: order
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.CustomerListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List customers
      tags:
      - partner-customers
    post:
      consumes:
      - application/json
      description: Creates a new customer. Email is required and must be unique within
        the company (409 on duplicates).
      parameters:
      - description: Customer to create
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.CustomerCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.CustomerItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a customer
      tags:
      - partner-customers
  /partner/v1/customers/{id}:
    get:
      description: Returns one customer by id. Ids belonging to another company return
        404.
      parameters:
      - description: Customer id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.CustomerItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a customer
      tags:
      - partner-customers
    patch:
      consumes:
      - application/json
      description: Updates allowlisted customer fields (contact info, address, code,
        active). Unknown fields are rejected. Changing the email to one that belongs
        to another customer returns 409.
      parameters:
      - description: Customer id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Fields to update
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.CustomerPatchRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.CustomerItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a customer
      tags:
      - partner-customers
  /partner/v1/customers/{id}/merge:
    post:
      consumes:
      - application/json
      description: Marks the customer in the path as merged into survivorId, relinks
        all of its leads/quotes/orders to the survivor, and emits a customer.merged
        event. The merged customer keeps resolving on GET (merged=true, mergedInto
        set) but disappears from lists (unless includeMerged=true) and rejects writes
        with 409. Supports Idempotency-Key.
      parameters:
      - description: Duplicate (loser) customer id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Merge target
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.CustomerMergeRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.CustomerMergeResponse'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Merge a duplicate customer into a survivor
      tags:
      - partner-customers
  /partner/v1/documents:
    get:
      description: Returns the file metadata (contract PDFs, previews, photos) linked
        to one order, quote, or work order. Download the bytes via GET /partner/v1/documents/{id}/download.
      parameters:
      - description: order | quote | workOrder
        in: query
        name: entityType
        required: true
        type: string
      - description: Entity id (24-char hex)
        in: query
        name: entityId
        required: true
        type: string
      - description: Filter by stored category (e.g. Contract)
        in: query
        name: type
        type: string
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.DocumentListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List documents for an entity
      tags:
      - partner-documents
  /partner/v1/documents/{id}/download:
    get:
      description: Returns a presigned URL valid for 10 minutes. The URL is single-purpose
        — request a fresh one for each download.
      parameters:
      - description: Document id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.DocumentDownloadResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a document download link
      tags:
      - partner-documents
  /partner/v1/domains:
    get:
      description: 'Returns the company''s white-label storefront domains (subdomains
        configured on DNS integrations) with their per-location assignments: hidePrices,
        defaultForStore, and product/size mappings.'
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Only domains assigned to this location id, with locations narrowed
          to it
        in: query
        name: locationId
        type: string
      - description: true → only location entries flagged Default For Store (domains
          without one are dropped)
        in: query
        name: defaultForStore
        type: boolean
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.DomainListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List domains
      tags:
      - partner-domains
  /partner/v1/events:
    get:
      description: Returns the company's change events after the given cursor, oldest
        first. Persist nextCursor and poll again to consume the feed losslessly. Events
        are retained for ~60 days.
      parameters:
      - description: Last consumed event id; omit to start from the oldest retained
          event
        in: query
        name: cursor
        type: string
      - description: Comma-separated event types to include (e.g. order.status_changed,payment.paid)
        in: query
        name: types
        type: string
      - default: 100
        description: Max events per page (max 200)
        in: query
        name: limit
        type: integer
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.EventListResponse'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Poll the change-event feed
      tags:
      - partner-events
  /partner/v1/events/{id}/redeliver:
    post:
      description: Re-enqueues one event to every active webhook subscription whose
        type filter matches. Use after fixing a broken endpoint. Delivery is at-least-once
        — dedupe by event id.
      parameters:
      - description: Event id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "202":
          description: Accepted
          schema:
            $ref: '#/definitions/partnerapi.EventRedeliverResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "503":
          description: Service Unavailable
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Redeliver an event
      tags:
      - partner-events
  /partner/v1/leads:
    get:
      description: Returns the authenticated company's leads with filtering, sorting,
        and pagination. All field names are clean, human-readable keys — internal
        codes never appear.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive substring match on customer name, order number,
          email, phone, or salesperson
        in: query
        name: search
        type: string
      - description: Filter by status (e.g. Open, Active, Sold, Lost, Retired)
        in: query
        name: status
        type: string
      - description: Sales location id or slug
        in: query
        name: location
        type: string
      - description: Filter by customer email (substring)
        in: query
        name: customerEmail
        type: string
      - description: Filter by customer phone (substring)
        in: query
        name: customerPhone
        type: string
      - description: Filter by exact lead number
        in: query
        name: orderNumber
        type: string
      - description: Filter by salesperson email (substring)
        in: query
        name: salesperson
        type: string
      - description: Filter by external reference, key:value exact match
        in: query
        name: externalRef
        type: string
      - description: Created on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdFrom
        type: string
      - description: Created on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdTo
        type: string
      - description: Updated on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedFrom
        type: string
      - description: Updated on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedTo
        type: string
      - default: createdAt
        description: 'Sort field: orderNumber | customerName | status | total | createdAt
          | updatedAt'
        in: query
        name: sort
        type: string
      - default: desc
        description: 'Sort direction: asc | desc'
        in: query
        name: order
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LeadListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List leads
      tags:
      - partner-leads
    post:
      consumes:
      - application/json
      description: Creates a lead at the given sales location with the customer's
        contact information. When salespersonName/salespersonEmail are omitted, the
        location's lead-routing strategy (round-robin, availability, skill-based)
        auto-assigns a salesperson — the same routing in-stock quote creation uses.
      parameters:
      - description: Sales location + customer contact
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.LeadCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.LeadItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a lead
      tags:
      - partner-leads
  /partner/v1/leads/{id}:
    get:
      description: Returns one lead by id. Ids belonging to another company return
        404.
      parameters:
      - description: Lead id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LeadItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a lead
      tags:
      - partner-leads
    patch:
      consumes:
      - application/json
      description: Updates allowlisted lead fields (salesLocation, salespersonName,
        salespersonEmail). Unknown fields are rejected.
      parameters:
      - description: Lead id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Fields to update
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.LeadPatchRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LeadItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a lead
      tags:
      - partner-leads
  /partner/v1/leads/{id}/status:
    post:
      consumes:
      - application/json
      description: Moves a lead between allowlisted statuses (Open ↔ Lost/Retired).
        Other transitions are rejected.
      parameters:
      - description: Lead id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Target status
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.PartnerStatusRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LeadItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a lead's status
      tags:
      - partner-leads
  /partner/v1/leads/{id}/status-history:
    get:
      description: Returns the lead's status transitions, newest first, with the acting
        user and action description.
      parameters:
      - description: Lead id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.StatusHistoryResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get status history for a lead
      tags:
      - partner-leads
  /partner/v1/locations:
    get:
      description: Returns the authenticated company's locations (sales lots, plants,
        warehouses) with filtering, sorting, and pagination.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive substring match on name, code, or city
        in: query
        name: search
        type: string
      - description: Filter by active flag
        in: query
        name: active
        type: boolean
      - description: Filter by sales-lot flag
        in: query
        name: salesLot
        type: boolean
      - description: Filter by plant flag
        in: query
        name: plant
        type: boolean
      - description: Filter by exact region label (dealer-defined)
        in: query
        name: region
        type: string
      - default: createdAt
        description: 'Sort field: name | code | city | createdAt | updatedAt'
        in: query
        name: sort
        type: string
      - default: desc
        description: 'Sort direction: asc | desc'
        in: query
        name: order
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LocationListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List locations
      tags:
      - partner-locations
    post:
      consumes:
      - application/json
      description: Creates a new location. Name is required, plus either an address
        or a latitude/longitude pair. The location code must be unique within the
        company.
      parameters:
      - description: Location to create
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.LocationCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.LocationItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a location
      tags:
      - partner-locations
  /partner/v1/locations/{id}:
    get:
      description: Returns one location by id. Ids belonging to another company return
        404.
      parameters:
      - description: Location id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LocationItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a location
      tags:
      - partner-locations
    patch:
      consumes:
      - application/json
      description: Updates allowlisted location fields. Latitude and longitude must
        be provided together. Unknown fields are rejected.
      parameters:
      - description: Location id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Fields to update
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.LocationPatchRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LocationItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a location
      tags:
      - partner-locations
  /partner/v1/locations/{id}/domains:
    get:
      description: Returns the domains the given location is assigned to, with each
        domain's locations narrowed to that location. Ids belonging to another company
        return 404.
      parameters:
      - description: Location id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: true → only the entry flagged Default For Store
        in: query
        name: defaultForStore
        type: boolean
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.DomainListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List a location's domains
      tags:
      - partner-domains
  /partner/v1/lot-stock:
    get:
      description: Returns the authenticated company's ready-to-sell physical inventory
        (units sitting on a lot, rental returns, or immediate-sale units).
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - default: ALL
        description: ALL | Lot Stock | Rental Return | Immediate Sale (case-insensitive;
          hyphens accepted, e.g. lot-stock)
        in: query
        name: purchaseType
        type: string
      - description: Location id or slug
        in: query
        name: location
        type: string
      - description: Filter by the locations' exact region label (dealer-defined)
        in: query
        name: region
        type: string
      - description: Case-insensitive substring match on serial number or title
        in: query
        name: search
        type: string
      - default: createdAt
        description: 'Sort field: serialNumber | title | price | createdAt'
        in: query
        name: sort
        type: string
      - default: desc
        description: 'Sort direction: asc | desc'
        in: query
        name: order
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LotStockResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List on-lot inventory
      tags:
      - partner-inventory
  /partner/v1/me:
    get:
      description: Returns the company, credential type, granted scopes, and rate-limit
        configuration for the calling credential. Requires any valid Partner API credential
        — no specific scope.
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.MeResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Introspect the authenticated credential
      tags:
      - partner-meta
  /partner/v1/orders:
    get:
      description: Returns the authenticated company's sales orders (records that
        are neither leads nor quotes) with filtering, sorting, and pagination.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive substring match on customer name, order number,
          email, phone, or salesperson
        in: query
        name: search
        type: string
      - description: Filter by status (e.g. Unsubmitted, Unprocessed, Processed, On
          hold, Closed)
        in: query
        name: status
        type: string
      - description: Sales location id or slug
        in: query
        name: location
        type: string
      - description: Filter by customer email (substring)
        in: query
        name: customerEmail
        type: string
      - description: Filter by customer phone (substring)
        in: query
        name: customerPhone
        type: string
      - description: Filter by exact order number
        in: query
        name: orderNumber
        type: string
      - description: Filter by salesperson email (substring)
        in: query
        name: salesperson
        type: string
      - description: Filter by external reference, key:value exact match
        in: query
        name: externalRef
        type: string
      - description: 'Filter by payment type: rto | cash'
        in: query
        name: paymentType
        type: string
      - description: Filter by serial number (substring)
        in: query
        name: serialNumber
        type: string
      - description: Created on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdFrom
        type: string
      - description: Created on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdTo
        type: string
      - description: Updated on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedFrom
        type: string
      - description: Updated on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedTo
        type: string
      - default: createdAt
        description: 'Sort field: orderNumber | customerName | status | total | createdAt
          | updatedAt'
        in: query
        name: sort
        type: string
      - default: desc
        description: 'Sort direction: asc | desc'
        in: query
        name: order
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.OrderListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List sales orders
      tags:
      - partner-orders
    post:
      consumes:
      - application/json
      description: 'Creates a full sales order: customer (existing id or inline auto-create),
        location, base product (model + optional size), optional upgrade lines, pricing
        header, optional configurator payload, and delivery address. Runs the same
        pipeline as the portal (order number, detail rows, configurator link, status
        log, ledger snapshot, audit, CRM). New orders start in status "Unsubmitted".
        Send an Idempotency-Key header to make retries safe.'
      parameters:
      - description: Unique key making the create retry-safe
        in: header
        name: Idempotency-Key
        type: string
      - description: Order payload
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.OrderCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.OrderItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a sales order
      tags:
      - partner-orders
  /partner/v1/orders/{id}:
    get:
      description: Returns one sales order by id. Ids belonging to another company
        return 404.
      parameters:
      - description: Sales order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.OrderItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a sales order
      tags:
      - partner-orders
    patch:
      consumes:
      - application/json
      description: Updates allowlisted sales order fields (customer contact, salesperson,
        salesLocation, delivery address). No pricing, serial, or work-order fields
        in v1. Orders with a contract out for signature reject changes with 409.
      parameters:
      - description: Sales order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Fields to update
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.SalesOrderPatchRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.OrderItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a sales order
      tags:
      - partner-orders
  /partner/v1/orders/{id}/contract:
    get:
      description: 'Returns the derived signing state of the order''s contract: status,
        version, who has signed and when, and the stored PDF document id. Signature
        images and customer data sheet fields are never included.'
      parameters:
      - description: Order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.ContractSummary'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get an order's contract summary
      tags:
      - partner-orders
  /partner/v1/orders/{id}/line-items:
    get:
      description: Returns the order's curated line items (upgrades, added/removed
        options, standard features) and the configured building summary.
      parameters:
      - description: Order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LineItemsResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get line items for an order
      tags:
      - partner-orders
    post:
      consumes:
      - application/json
      description: 'Adds one upgrade line (product reference + quantity/price) to
        the order. Idempotent on lineKey: re-sending the same key returns the existing
        line instead of duplicating it. Header pricing is not recalculated — PATCH
        the order''s pricing fields afterward.'
      parameters:
      - description: Order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Line item
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.LineItemCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.LineItemCreateResponse'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Add a line item to an order
      tags:
      - partner-orders
  /partner/v1/orders/{id}/line-items/{lineId}:
    delete:
      description: Removes one upgrade line by its line id (from GET line-items or
        the add response). The base product line cannot be deleted. Header pricing
        is not recalculated — PATCH the order's pricing fields afterward.
      parameters:
      - description: Order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Line id
        in: path
        name: lineId
        required: true
        type: string
      produces:
      - application/json
      responses:
        "204":
          description: No Content
          schema:
            type: string
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Delete a line item from an order
      tags:
      - partner-orders
  /partner/v1/orders/{id}/payment-links:
    post:
      consumes:
      - application/json
      description: 'Creates a Stripe Checkout session for the order and returns the
        hosted payment URL. The company''s Stripe integration must be connected and
        have payments enabled. When the customer pays, the webhook pipeline records
        the payment and emits payment.* events automatically. By default the customer
        also receives the payment-link email — pass "sendEmail": false to suppress
        it.'
      parameters:
      - description: Order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Payment link payload
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.PaymentLinkCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.PaymentLinkResponse'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "502":
          description: Bad Gateway
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a Stripe payment link for an order
      tags:
      - partner-payments
  /partner/v1/orders/{id}/payments:
    get:
      description: Returns the payment records linked to one sales order, newest first.
      parameters:
      - description: Order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.PaymentListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List an order's payments
      tags:
      - partner-orders
    post:
      consumes:
      - application/json
      description: 'Records a manual payment (cash, check, financed, or manual) against
        a sales order, through the same pipeline as the portal: payment record, localized
        mirror, payment-ledger recording (order balance recalculation), audit. Card/ACH
        payments are rejected — those records are created by the Stripe webhook pipeline
        (use payment-links instead). Status is always "paid". Send an Idempotency-Key
        header to make retries safe.'
      parameters:
      - description: Unique key making the create retry-safe
        in: header
        name: Idempotency-Key
        type: string
      - description: Order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Payment payload
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.PaymentCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.PaymentItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Record a manual payment on an order
      tags:
      - partner-payments
  /partner/v1/orders/{id}/status:
    post:
      consumes:
      - application/json
      description: Moves a sales order between allowlisted statuses (Unsubmitted →
        Unprocessed/On hold, Unprocessed ↔ On hold). "Processed" is blocked in v1.
        Submitting (→ Unprocessed) requires a serial number on the order or its linked
        work order.
      parameters:
      - description: Sales order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Target status
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.PartnerStatusRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.OrderItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a sales order's status
      tags:
      - partner-orders
  /partner/v1/orders/{id}/status-history:
    get:
      description: Returns the order's status transitions, newest first, with the
        acting user and action description.
      parameters:
      - description: Order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.StatusHistoryResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get status history for an order
      tags:
      - partner-orders
  /partner/v1/payments:
    get:
      description: Returns the authenticated company's payment records, newest first,
        with order/status/date filters.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Filter by sales order id (24-char hex)
        in: query
        name: orderId
        type: string
      - description: Filter by status (case-insensitive exact match, e.g. paid, Refunded)
        in: query
        name: status
        type: string
      - description: Created on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdFrom
        type: string
      - description: Created on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdTo
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.PaymentListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List payments
      tags:
      - partner-payments
  /partner/v1/payments/{id}:
    get:
      description: Returns one payment record by id. Ids belonging to another company
        return 404.
      parameters:
      - description: Payment id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.PaymentItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a payment
      tags:
      - partner-payments
  /partner/v1/products:
    get:
      description: Returns the authenticated company's finished catalog products (raw
        materials and kits are excluded) with gallery image URLs, filtering, sorting,
        and pagination. For serialized on-lot units, use GET /partner/v1/lot-stock.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive substring match on name, SKU, or description
        in: query
        name: search
        type: string
      - description: Filter by SKU (substring)
        in: query
        name: sku
        type: string
      - description: Filter by active flag
        in: query
        name: active
        type: boolean
      - description: Created on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdFrom
        type: string
      - description: Created on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdTo
        type: string
      - description: Updated on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedFrom
        type: string
      - description: Updated on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedTo
        type: string
      - default: createdAt
        description: 'Sort field: name | sku | price | createdAt | updatedAt'
        in: query
        name: sort
        type: string
      - default: desc
        description: 'Sort direction: asc | desc'
        in: query
        name: order
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.ProductListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List products
      tags:
      - partner-inventory
    post:
      consumes:
      - application/json
      description: Creates a finished catalog product. Name is required. Optionally
        link it under an existing product line with lineId. Pricing/dimensions for
        configurable models should live on size children — see POST /partner/v1/products/{id}/sizes.
      parameters:
      - description: Product to create
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.ProductCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.ProductItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a product
      tags:
      - partner-inventory
  /partner/v1/products/{id}:
    get:
      description: Returns one finished catalog product by id with gallery image URLs.
        Ids belonging to another company — or to raw materials / kits — return 404.
      parameters:
      - description: Product id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.ProductItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a product
      tags:
      - partner-inventory
    patch:
      consumes:
      - application/json
      description: Updates allowlisted product fields (name, description, sku, price,
        width, length, active). Unknown fields are rejected. Raw materials, kits,
        and products hidden from the Partner API return 404.
      parameters:
      - description: Product id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Fields to update
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.ProductPatchRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.ProductItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a product
      tags:
      - partner-inventory
  /partner/v1/products/{id}/sizes:
    post:
      consumes:
      - application/json
      description: Creates a size child under a catalog product (width, length, price)
        and links it with the finished-good relationship — the same structure the
        portal's size editor writes. Size children carry the model's real pricing
        and dimensions.
      parameters:
      - description: Parent product id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Size to create
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.ProductSizeCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.ProductSizeItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Add a size to a product
      tags:
      - partner-inventory
  /partner/v1/quotes:
    get:
      description: Returns the authenticated company's quotes with filtering, sorting,
        and pagination.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive substring match on customer name, quote number,
          email, phone, or salesperson
        in: query
        name: search
        type: string
      - description: Filter by status (e.g. Active, Sold, Lost, Retired)
        in: query
        name: status
        type: string
      - description: Sales location id or slug
        in: query
        name: location
        type: string
      - description: Filter by customer email (substring)
        in: query
        name: customerEmail
        type: string
      - description: Filter by customer phone (substring)
        in: query
        name: customerPhone
        type: string
      - description: Filter by exact quote number
        in: query
        name: orderNumber
        type: string
      - description: Filter by salesperson email (substring)
        in: query
        name: salesperson
        type: string
      - description: Filter by external reference, key:value exact match
        in: query
        name: externalRef
        type: string
      - description: true = only quotes converted to an order, false = only unconverted
        in: query
        name: converted
        type: boolean
      - description: Created on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdFrom
        type: string
      - description: Created on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdTo
        type: string
      - description: Updated on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedFrom
        type: string
      - description: Updated on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedTo
        type: string
      - default: createdAt
        description: 'Sort field: orderNumber | customerName | status | total | createdAt
          | updatedAt'
        in: query
        name: sort
        type: string
      - default: desc
        description: 'Sort direction: asc | desc'
        in: query
        name: order
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.QuoteListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List quotes
      tags:
      - partner-quotes
    post:
      consumes:
      - application/json
      description: Creates a quote for an on-lot building identified by serial number
        or work order id. The unit's work order is linked to the quote (copying serial
        number, product lines, and pricing), the sales location is assigned, and lead
        routing auto-assigns a salesperson when the location has routing configured.
        Newly created customers are synced to any connected CRM. Fails with 409 when
        the unit already has an active quote or order, is a stock template, or is
        cancelled.
      parameters:
      - description: In-stock unit reference + customer
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.QuoteCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.QuoteItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a quote from an in-stock unit
      tags:
      - partner-quotes
  /partner/v1/quotes/{id}:
    get:
      description: Returns one quote by id. Ids belonging to another company return
        404.
      parameters:
      - description: Quote id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.QuoteItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a quote
      tags:
      - partner-quotes
    patch:
      consumes:
      - application/json
      description: Updates allowlisted quote fields (customer contact, salesperson,
        salesLocation, delivery address). No pricing fields in v1.
      parameters:
      - description: Quote id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Fields to update
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.SalesOrderPatchRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.QuoteItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a quote
      tags:
      - partner-quotes
  /partner/v1/quotes/{id}/convert:
    post:
      consumes:
      - application/json
      description: 'Converts an open quote into a real sales order: allocates the
        next order number, clones the quote''s product lines and configurator onto
        the new order, marks the quote Sold with a back-reference, and re-links the
        quote''s work order to the order. The new order starts in the Unsubmitted
        status. Fails with 409 when the quote is already Sold/Cancelled/Deleted or
        an active order already exists for it. Requires the orders write scope.'
      parameters:
      - description: Quote id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Optional salesperson overrides
        in: body
        name: body
        schema:
          $ref: '#/definitions/partnerapi.QuoteConvertRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.OrderItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Convert a quote to a sales order
      tags:
      - partner-quotes
  /partner/v1/quotes/{id}/line-items:
    get:
      description: Returns the quote's curated line items (upgrades, added/removed
        options, standard features) and the configured building summary.
      parameters:
      - description: Quote id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.LineItemsResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get line items for a quote
      tags:
      - partner-quotes
    post:
      consumes:
      - application/json
      description: 'Adds one upgrade line (product reference + quantity/price) to
        the quote. Idempotent on lineKey: re-sending the same key returns the existing
        line instead of duplicating it. Header pricing is not recalculated — PATCH
        the quote''s pricing fields afterward.'
      parameters:
      - description: Quote id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Line item
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.LineItemCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.LineItemCreateResponse'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Add a line item to a quote
      tags:
      - partner-quotes
  /partner/v1/quotes/{id}/line-items/{lineId}:
    delete:
      description: Removes one upgrade line by its line id (from GET line-items or
        the add response). The base product line cannot be deleted. Header pricing
        is not recalculated — PATCH the quote's pricing fields afterward.
      parameters:
      - description: Quote id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Line id
        in: path
        name: lineId
        required: true
        type: string
      produces:
      - application/json
      responses:
        "204":
          description: No Content
          schema:
            type: string
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Delete a line item from a quote
      tags:
      - partner-quotes
  /partner/v1/quotes/{id}/status:
    post:
      consumes:
      - application/json
      description: Moves a quote between allowlisted statuses (Open → Active/Lost/Retired,
        Active → Lost/Retired, Lost → Active, Retired → Active). Other transitions
        are rejected.
      parameters:
      - description: Quote id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Target status
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.PartnerStatusRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.QuoteItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a quote's status
      tags:
      - partner-quotes
  /partner/v1/quotes/{id}/status-history:
    get:
      description: Returns the quote's status transitions, newest first, with the
        acting user and action description.
      parameters:
      - description: Quote id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.StatusHistoryResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get status history for a quote
      tags:
      - partner-quotes
  /partner/v1/roles:
    get:
      description: Returns the company's assignable RBAC roles, for discovering valid
        roleId values for user create/update.
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.RolesResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List roles
      tags:
      - partner-users
  /partner/v1/stock-templates:
    get:
      description: Returns the company's stock templates — buildable catalog designs
        (not physical inventory; see /lot-stock for on-lot units). Each entry carries
        images, pricing, public tags, and a structuraUrl deep link to the interactive
        3D configurator view.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 60)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive match on work-order number, template name, or
          product name
        in: query
        name: search
        type: string
      - description: Comma-separated public tags; templates must carry all of them
        in: query
        name: tags
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.StockTemplateListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List stock templates
      tags:
      - partner-inventory
  /partner/v1/stock-templates/{id}:
    get:
      description: Returns one stock template by its work-order id. Ids belonging
        to another company (or non-template work orders) return 404.
      parameters:
      - description: Stock template work-order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.StockTemplateItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a stock template
      tags:
      - partner-inventory
  /partner/v1/users:
    get:
      description: Returns the authenticated company's users/salespeople with their
        location assignments and lead-routing pool membership.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive substring match on name or email
        in: query
        name: search
        type: string
      - description: Filter by active state
        in: query
        name: active
        type: boolean
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.UserListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List users
      tags:
      - partner-users
    post:
      consumes:
      - application/json
      description: 'Creates a company user (salesperson) through the portal''s own
        pipeline: profile, RBAC role, location assignments, and the invitation email
        (default on). When the email belongs to an existing ShedCloud account, that
        account is linked to the company instead of duplicated. The user''s Cognito
        login is created at their first sign-in via the invite — not by this call.'
      parameters:
      - description: User to create
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.UserCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.UserItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a user
      tags:
      - partner-users
  /partner/v1/users/{id}:
    get:
      description: Returns one company user by id. Ids belonging to another company
        return 404.
      parameters:
      - description: User id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.UserItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a user
      tags:
      - partner-users
    patch:
      consumes:
      - application/json
      description: 'Updates a company user: profile fields, RBAC role (roleId), location
        assignments (locationIds replaces; allLocations grants unrestricted access),
        and active (per-company enable/disable — the owner is protected). Empty/omitted
        fields are left untouched.'
      parameters:
      - description: User id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Fields to update
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.UserPatchRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.UserItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a user
      tags:
      - partner-users
  /partner/v1/webhook-deliveries:
    get:
      description: Returns the delivery attempt log, newest first, optionally narrowed
        to one event or subscription. Rows are retained ~30 days.
      parameters:
      - description: Filter by event id
        in: query
        name: eventId
        type: string
      - description: Filter by subscription id
        in: query
        name: subscriptionId
        type: string
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.WebhookDeliveryListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List webhook delivery attempts
      tags:
      - partner-events
  /partner/v1/work-orders:
    get:
      description: Returns the authenticated company's work orders (owned and shared)
        with filtering, sorting, and pagination.
      parameters:
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      - description: Case-insensitive substring match on serial number, title, customer
          name, or work order number
        in: query
        name: search
        type: string
      - description: Filter by status (e.g. Open build, Build in process, Finished
          good, Delivered)
        in: query
        name: status
        type: string
      - description: Building location id or slug
        in: query
        name: location
        type: string
      - description: Filter by serial number (substring)
        in: query
        name: serialNumber
        type: string
      - description: Filter by linked sales order number
        in: query
        name: orderNumber
        type: integer
      - description: Filter by linked sales order id (24-char hex)
        in: query
        name: linkedOrderId
        type: string
      - description: Filter by external reference, key:value exact match
        in: query
        name: externalRef
        type: string
      - description: Created on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdFrom
        type: string
      - description: Created on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: createdTo
        type: string
      - description: Updated on/after (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedFrom
        type: string
      - description: Updated on/before (RFC 3339 or YYYY-MM-DD)
        in: query
        name: updatedTo
        type: string
      - default: createdAt
        description: 'Sort field: workOrderNumber | serialNumber | status | createdAt
          | updatedAt'
        in: query
        name: sort
        type: string
      - default: desc
        description: 'Sort direction: asc | desc'
        in: query
        name: order
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.WorkOrderListResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: List work orders
      tags:
      - partner-work-orders
    post:
      consumes:
      - application/json
      description: 'Creates a manufacturing work order the same way the portal''s
        building wizard does: status starts in "Customer Care", the work order number
        is allocated automatically, and the friendly type enums map to the internal
        build-type flags. An optional sizeId attaches a product size (creating the
        manufacturing BOM unless purchaseType is existing-physical-inventory). Serial
        numbers are enforced unique per company. Send an Idempotency-Key header to
        make retries safe.'
      parameters:
      - description: Unique key making the create retry-safe
        in: header
        name: Idempotency-Key
        type: string
      - description: Work order payload
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.WorkOrderCreateRequest'
      produces:
      - application/json
      responses:
        "201":
          description: Created
          schema:
            $ref: '#/definitions/partnerapi.WorkOrderItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Create a work order
      tags:
      - partner-work-orders
  /partner/v1/work-orders/{id}:
    get:
      description: Returns one work order by id. Ids belonging to another company
        return 404.
      parameters:
      - description: Work order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.WorkOrderItem'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get a work order
      tags:
      - partner-work-orders
    patch:
      consumes:
      - application/json
      description: Updates allowlisted work order fields (title, description, buildingLocation,
        promisedDate). No serial number changes in v1.
      parameters:
      - description: Work order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Fields to update
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.WorkOrderPatchRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.WorkOrderItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a work order
      tags:
      - partner-work-orders
  /partner/v1/work-orders/{id}/status:
    post:
      consumes:
      - application/json
      description: 'Moves a work order along the production flow: Open build → Build
        in process → Finished good → Delivered. Other transitions (including "Requested
        materials", which transfers inventory) are rejected.'
      parameters:
      - description: Work order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - description: Target status
        in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/partnerapi.PartnerStatusRequest'
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.WorkOrderItem'
        "400":
          description: Bad Request
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "409":
          description: Conflict
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Update a work order's status
      tags:
      - partner-work-orders
  /partner/v1/work-orders/{id}/status-history:
    get:
      description: Returns the work order's status transitions, newest first, with
        the acting user and action description.
      parameters:
      - description: Work order id (24-char hex)
        in: path
        name: id
        required: true
        type: string
      - default: 1
        description: Page number
        in: query
        name: page
        type: integer
      - default: 50
        description: Page size (max 100)
        in: query
        name: limit
        type: integer
      produces:
      - application/json
      responses:
        "200":
          description: OK
          schema:
            $ref: '#/definitions/partnerapi.StatusHistoryResponse'
        "401":
          description: Unauthorized
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "403":
          description: Forbidden
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
        "404":
          description: Not Found
          schema:
            $ref: '#/definitions/partnerapi.ErrorResponse'
      security:
      - PartnerBearer: []
      summary: Get status history for a work order
      tags:
      - partner-status-history
securityDefinitions:
  BasicAuth:
    type: basic
  PartnerBearer:
    description: API key or OAuth access token, e.g. "Bearer sc_live_..." or "Bearer
      sc_at_...".
    in: header
    name: Authorization
    type: apiKey
swagger: "2.0"
