openapi: 3.1.0
info:
  title: Resellers
  version: "1"
  description: Portfolio summary and onboarding status across your reseller’s merchants.
tags:
  - name: Reseller portfolio
    description: Summary and onboarding status across all merchants under your reseller.
paths:
  /v1/onboarding/reseller-merchants-summary:
    post:
      operationId: getResellerMerchantsSummary
      summary: Get onboarding totals across the reseller's merchants
      description: "Returns counts of the reseller's merchants by onboarding state:
        total, fully verified, verification tier, staggered onboarding, and
        closed or suspended. For API key callers the reseller is always taken
        from the key; `resellerId` may be omitted, and if sent it must match or
        the response is 403. The body must be valid JSON, so send `{}` when
        there is nothing to pass."
      tags:
        - Reseller portfolio
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CpResellerMerchantsSummaryRequest"
            examples:
              keyScope:
                summary: Use the API key's reseller
                value: {}
              explicitReseller:
                summary: Name the reseller explicitly (must match the key)
                value:
                  resellerId: 8007
      responses:
        "200":
          description: Onboarding counts for the reseller's merchants.
          headers:
            X-Onboarding-Cache:
              description: HIT when served from the in-memory cache, otherwise MISS.
              schema:
                type: string
                enum:
                  - HIT
                  - MISS
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CpResellerMerchantsSummaryResponse"
              examples:
                summaryFound:
                  summary: Portfolio onboarding counts
                  value:
                    data:
                      summary:
                        total_merchants: 42
                        staggered_onboarding: "30"
                        up_to1000: "8"
                        up_to5000: "12"
                        fully_verified: "25"
                        closed_or_suspended: "3"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: The API key has no reseller, or the body names a different reseller
            than the key's.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                resellerMismatch:
                  summary: resellerId does not match the key
                  value:
                    error:
                      code: FORBIDDEN
                      message: Access denied to this reseller
        "422":
          description: The body is not valid JSON, or resellerId is not a positive integer.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                invalidJson:
                  summary: Body missing or not JSON
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: Invalid JSON body
                invalidResellerId:
                  summary: resellerId is not a positive integer
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: Invalid reseller_id
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    servers:
      - url: https://sandbox-portal.datmanlabs.com/api
        description: Sandbox
  /v1/onboarding/reseller-merchants-onboarding-status:
    post:
      operationId: listResellerMerchantsOnboardingStatus
      summary: List onboarding status for the reseller's merchants
      description: "Returns the onboarding record of each of the reseller's merchants:
        business, owner, bank, payout, account and terms-of-service status,
        verification tier, outstanding errors, onboarding links and tags, plus
        every tag the reseller has defined. Set `enablePagination: true` to
        page, search, sort and filter; without it every merchant is returned and
        only `isLiableOnly` and sorting apply. For API key callers the reseller
        is always taken from the key; `resellerId` is optional and must match if
        sent."
      tags:
        - Reseller portfolio
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CpResellerMerchantsStatusRequest"
            examples:
              firstPage:
                summary: First page of 20, newest first
                value:
                  enablePagination: true
                  page: 1
                  limit: 20
              searchActive:
                summary: Search active merchants by name or ID
                value:
                  enablePagination: true
                  page: 1
                  limit: 10
                  search: example pizza
                  activeOnly: true
                  sortField: createdAt
                  sortOrder: desc
              wholePortfolio:
                summary: Every merchant, no pagination
                value: {}
      responses:
        "200":
          description: Merchant onboarding rows, all tags defined by the reseller, and the
            total. With pagination, `total` is the number of matching merchants
            across all pages; without it, `total` is the number of rows
            returned.
          headers:
            X-Onboarding-Cache:
              description: HIT when served from the in-memory cache, otherwise MISS.
              schema:
                type: string
                enum:
                  - HIT
                  - MISS
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CpResellerMerchantsStatusResponse"
              examples:
                merchantsFound:
                  summary: One page with one merchant
                  value:
                    data:
                      rows:
                        - id: 5501
                          merchant_id: 123456
                          account_holder_id: AH00000000000000000000001
                          reseller_name: Example Reseller Ltd
                          country: United Kingdom
                          merchant_name: Example Pizza Co
                          trading_name: Example Pizza Co
                          business_status: 1
                          owner_status: 1
                          bank_status: 0
                          payout_status: 0
                          account_status: 1
                          tos_status: 1
                          verification_tier: Tier 1
                          internal_tags: null
                          onboarding_link: https://onboarding.example.com/a1b2c3d4
                          overview_page: null
                          average_weekly_orders: 120
                          due_in_days: 14
                          raw_data: null
                          list_of_errors: null
                          note: null
                          get_all_low_errors: null
                          is_reseller: 0
                          created_at: 2026-09-02T10:15:00.000Z
                          hosted_onboarding_enabled: 1
                      all_tags: []
                      total: 1
                      summary: null
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          description: The API key has no reseller, or the body names a different reseller
            than the key's.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                resellerMismatch:
                  summary: resellerId does not match the key
                  value:
                    error:
                      code: FORBIDDEN
                      message: Access denied to this reseller
        "422":
          description: The request body failed validation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                invalidJson:
                  summary: Body missing or not JSON
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: Invalid JSON body
                invalidSortField:
                  summary: Unsupported sortField
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: "Invalid sortField: merchantName"
                invalidSortOrder:
                  summary: sortOrder is not asc or desc
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: "Invalid sortOrder: newest"
                invalidResellerId:
                  summary: resellerId is not a positive integer
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: Invalid reseller_id
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "503":
          $ref: "#/components/responses/ServiceUnavailable"
    servers:
      - url: https://sandbox-portal.datmanlabs.com/api
        description: Sandbox
components:
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable SCREAMING_SNAKE error code.
              examples:
                - UNAUTHORIZED
                - FORBIDDEN
                - VALIDATION_ERROR
                - RATE_LIMITED
                - INTERNAL_ERROR
                - DB_TIMEOUT
                - DB_CLIENT_CLOSED
            message:
              type: string
              description: Human-readable error message.
            details:
              description: Optional extra context about the error.
    CpResellerMerchantsSummaryRequest:
      type: object
      description: Keys may be camelCase or snake_case (reseller_id).
      properties:
        resellerId:
          type:
            - integer
            - string
          description: Optional for API key callers. If sent it must be a positive integer
            (or numeric string) equal to the key's reseller, otherwise 403 (or
            422 if malformed).
    CpResellerMerchantsSummary:
      type: object
      description: Counts over the reseller's merchants. All counts except
        total_merchants are SQL SUM() values and are likely returned as numeric
        strings; they are null when the reseller has no merchants.
      properties:
        total_merchants:
          type: integer
        staggered_onboarding:
          type:
            - string
            - integer
            - "null"
          description: Merchants with account and payout status both non-zero.
        up_to1000:
          type:
            - string
            - integer
            - "null"
          description: Merchants on verification tier "Tier 1".
        up_to5000:
          type:
            - string
            - integer
            - "null"
          description: Merchants on verification tier "Tier 2".
        fully_verified:
          type:
            - string
            - integer
            - "null"
          description: Merchants with bank, business, owner and ToS verified and account
            and payout active.
        closed_or_suspended:
          type:
            - string
            - integer
            - "null"
          description: Merchants with account_status 0.
    CpResellerMerchantsSummaryResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - summary
          properties:
            summary:
              oneOf:
                - $ref: "#/components/schemas/CpResellerMerchantsSummary"
                - type: "null"
    CpResellerMerchantsStatusRequest:
      type: object
      description: Keys may be camelCase or snake_case (reseller_id,
        enable_pagination, and so on).
      properties:
        resellerId:
          type:
            - integer
            - string
          description: Optional for API key callers; must match the key's reseller if sent.
        enablePagination:
          type: boolean
          default: false
          description: Turn on paging, search and the activeOnly/excludeMerchantId
            filters. When false every merchant is returned.
        page:
          type: integer
          minimum: 1
          default: 1
          description: Page number (pagination only).
        limit:
          type: integer
          minimum: 1
          default: 20
          description: Page size (pagination only). Not capped by the server.
        search:
          type: string
          default: ""
          description: Case-insensitive match on merchant name, trading name or merchant
            ID (pagination only).
        isLiableOnly:
          type: boolean
          default: false
          description: Only merchants flagged as resellers (liable accounts).
        activeOnly:
          type: boolean
          default: false
          description: Exclude closed or suspended merchants (account_status 0).
            Pagination only.
        excludeMerchantId:
          type: integer
          description: Leave this merchant out of the results. Pagination only.
        sortField:
          type: string
          enum:
            - createdAt
          description: Sort by merchant creation time. When omitted, rows are ordered by
            onboarding record, newest first.
        sortOrder:
          type: string
          enum:
            - asc
            - desc
          default: desc
    CpResellerMerchantOnboardingRow:
      type: object
      description: One merchant's onboarding record.
      properties:
        id:
          type: integer
          description: Onboarding record ID.
        merchant_id:
          type: integer
        account_holder_id:
          type:
            - string
            - "null"
          description: Account holder ID at the payment provider.
        reseller_name:
          type:
            - string
            - "null"
        country:
          type:
            - string
            - "null"
        merchant_name:
          type:
            - string
            - "null"
        trading_name:
          type:
            - string
            - "null"
        business_status:
          type:
            - integer
            - "null"
          description: 1 when business details are verified.
        owner_status:
          type:
            - integer
            - "null"
          description: 1 when owner details are verified.
        bank_status:
          type:
            - integer
            - "null"
          description: 1 when bank details are verified.
        payout_status:
          type:
            - integer
            - "null"
          description: 0 when payouts are not enabled.
        account_status:
          type:
            - integer
            - "null"
          description: 0 when the account is closed or suspended.
        tos_status:
          type:
            - integer
            - "null"
          description: 1 when the terms of service are accepted.
        verification_tier:
          type:
            - string
            - "null"
          examples:
            - Tier 1
            - Tier 2
        internal_tags:
          description: Tags assigned to the merchant (column type not declared in the repo).
        onboarding_link:
          type:
            - string
            - "null"
        overview_page:
          type:
            - string
            - "null"
        average_weekly_orders:
          type:
            - number
            - "null"
        due_in_days:
          type:
            - number
            - "null"
        raw_data:
          description: Raw onboarding data from the provider (column type not declared in
            the repo).
        list_of_errors:
          description: Outstanding verification errors (column type not declared in the
            repo).
        note:
          type:
            - string
            - "null"
        get_all_low_errors:
          description: Low-severity verification errors (column type not declared in the
            repo).
        is_reseller:
          type:
            - integer
            - "null"
          description: 1 when the merchant is a liable (reseller) account.
        created_at:
          type:
            - string
            - "null"
          description: Merchant creation time (ISO string).
        hosted_onboarding_enabled:
          type:
            - integer
            - "null"
        payment_provider:
          type:
            - string
            - "null"
          description: Payment provider from the gateway. Omitted when that lookup is
            unavailable.
    CpResellerMerchantsStatusResponse:
      type: object
      required:
        - data
      properties:
        data:
          type: object
          required:
            - rows
            - all_tags
            - total
            - summary
          properties:
            rows:
              type: array
              items:
                $ref: "#/components/schemas/CpResellerMerchantOnboardingRow"
            all_tags:
              type: array
              description: Every onboarding tag the reseller has defined. Row columns are
                dynamic (SELECT *), with keys snake_cased.
              items:
                type: object
                additionalProperties: true
            total:
              type: integer
              description: Total matching merchants (paginated) or rows returned
                (unpaginated).
            summary:
              type: "null"
              description: Always null on this endpoint; use reseller-merchants-summary
                instead.
  responses:
    Unauthorized:
      description: Missing, invalid, expired or revoked credentials.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: UNAUTHORIZED
              message: Invalid, expired, or revoked API key
    RateLimited:
      description: The per-reseller rate limit was exceeded.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: RATE_LIMITED
              message: Rate limit exceeded
    InternalError:
      description: The query or a required configuration failed. Underlying details
        are not returned.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: INTERNAL_ERROR
              message: Query failed
    ServiceUnavailable:
      description: The database timed out or closed the connection. Safe to retry.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: DB_TIMEOUT
              message: The database did not respond in time. Please retry.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: api_key
      description: Partner API key issued by Datman.
