openapi: 3.1.0
info:
  title: Balances & Statements
  version: "1"
  description: Merchant balances and payment, refund and net totals.
tags:
  - name: Balances
    description: Merchant balances and payment, refund and net totals.
paths:
  /v1/merchants/{merchantId}/balance:
    get:
      operationId: getMerchantBalance
      summary: Get account balance
      description: Returns the merchant's current available balance in major currency
        units, rounded to 2 decimal places. The balance includes card and
        payment takings, internal transfers and in-transit provider funds, minus
        completed payouts, depending on the merchant's country and payout setup.
        `pending` and `on_hold` are always 0. Returns all zeros when the
        merchant has no gateway customer record. The merchant must belong to
        your reseller, otherwise 404.
      tags:
        - Balances
      security:
        - ApiKeyAuth: []
      parameters:
        - name: merchantId
          in: path
          required: true
          description: Numeric merchant id. Must be a positive integer belonging to your
            reseller.
          schema:
            type: integer
            minimum: 1
          example: 123456
      responses:
        "200":
          description: Current balance.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CpMerchantBalanceResponse"
              examples:
                balance:
                  summary: Merchant with an available balance
                  value:
                    data:
                      available: 2345.67
                      pending: 0
                      on_hold: 0
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: The merchant does not belong to your reseller.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                notFound:
                  summary: Merchant not found or not in your reseller
                  value:
                    error:
                      code: NOT_FOUND
                      message: Merchant not found
        "422":
          description: The merchantId path segment is not numeric.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                invalidMerchantId:
                  summary: Non-numeric merchant id
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: merchantId must be numeric
        "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/transactions/merchant-summary:
    post:
      operationId: getMerchantTransactionSummary
      summary: Get payment and refund totals for a merchant
      description: Returns the total of successful payments (net), the total refunded
        (including chargebacks), and the difference, for one merchant between
        fromDate and toDate (calendar dates, at most 90 days apart). For API key
        callers the reseller is taken from the key and the merchant must belong
        to it, otherwise 404. Body keys may be camelCase or snake_case.
      tags:
        - Balances
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CpMerchantSummaryRequest"
            examples:
              lastTwoWeeks:
                summary: Totals for one merchant over two weeks
                value:
                  merchantId: 123456
                  fromDate: 2026-10-01
                  toDate: 2026-10-14
                  timezone: Europe/London
      responses:
        "200":
          description: The merchant's totals. is_liable is true when the merchant is a
            liable account (totals then come from its own payments regardless of
            reseller scope). When the reseller has no queryable configuration
            the totals are zero and is_liable is omitted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CpMerchantTransactionSummaryResponse"
              examples:
                summaryFound:
                  summary: Totals for the window
                  value:
                    data:
                      payment_total: 1250.4
                      refund_total: 35
                      net: 1215.4
                      is_liable: false
                noScope:
                  summary: Reseller has no queryable configuration
                  value:
                    data:
                      payment_total: 0
                      refund_total: 0
                      net: 0
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: API key callers only. The merchant is not mapped to the key's
            reseller.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                merchantNotFound:
                  summary: Unknown or out-of-scope merchant
                  value:
                    error:
                      code: NOT_FOUND
                      message: Merchant not found
        "422":
          description: The request body failed validation.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                invalidMerchantId:
                  summary: merchantId missing or not numeric
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: merchantId must be an integer
                invalidFromDate:
                  summary: fromDate is not YYYY-MM-DD
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: fromDate must be YYYY-MM-DD
                rangeTooLong:
                  summary: Window longer than 90 days
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: Date range cannot exceed 90 days
                notAnObject:
                  summary: Body is missing or not a JSON object
                  value:
                    error:
                      code: VALIDATION_ERROR
                      message: Request body must be a JSON object
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          description: The summary query or a required configuration failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              examples:
                summaryFailed:
                  summary: Summary query failed
                  value:
                    error:
                      code: INTERNAL_ERROR
                      message: Summary query failed
        "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.
    CpMerchantBalance:
      type: object
      required:
        - available
        - pending
        - on_hold
      properties:
        available:
          type: number
          description: Available balance in major currency units, rounded to 2 decimal
            places.
        pending:
          type: number
          description: Always 0.
        on_hold:
          type: number
          description: Always 0.
    CpMerchantBalanceResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/CpMerchantBalance"
    CpMerchantSummaryRequest:
      type: object
      description: Keys may be camelCase or snake_case (merchant_id, from_date, to_date).
      required:
        - merchantId
        - fromDate
        - toDate
      properties:
        merchantId:
          type:
            - integer
            - string
          description: Merchant to summarize. A numeric string is accepted.
        fromDate:
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
          description: Start calendar date (YYYY-MM-DD), inclusive.
        toDate:
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
          description: End calendar date (YYYY-MM-DD), inclusive. At most 90 days after
            fromDate.
        timezone:
          type: string
          description: IANA timezone the dates are in. Recommended for API key callers;
            when omitted the dates are interpreted in UTC.
          examples:
            - Europe/London
        resellerId:
          type: integer
          description: Ignored for API key callers (the key's reseller is always used).
    CpMerchantTransactionSummary:
      type: object
      required:
        - payment_total
        - refund_total
        - net
      properties:
        payment_total:
          type: number
          description: Sum of net amounts of successful payments, in major units (2 dp).
        refund_total:
          type: number
          description: Sum of refunded and charged-back net amounts (positive), in major
            units (2 dp).
        net:
          type: number
          description: payment_total minus refund_total (2 dp).
        is_liable:
          type: boolean
          description: Whether the merchant is a liable account. Omitted when the reseller
            has no queryable scope.
    CpMerchantTransactionSummaryResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/CpMerchantTransactionSummary"
  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
    Forbidden:
      description: The caller may not access this route or reseller (for example the
        API key has no reseller, or the body names a different reseller).
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
          example:
            error:
              code: FORBIDDEN
              message: Reseller not allowed for this caller
    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.
