> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sandpay.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# List payments

> Cursor-paginated list of payments for the authenticated organisation.



## OpenAPI

````yaml /openapi/sandpay.yaml get /v1/payments
openapi: 3.1.0
info:
  title: SandPay API
  version: 0.5.3
  description: |
    Official public API for the SandPay Mobile Money sandbox. Simulate MTN,
    Orange, Moov, and Airtel payment flows across African markets without
    touching real money rails. The API is deterministic, low-latency, and
    designed to mirror the production behaviour of each operator so client
    integrations can be exercised end-to-end before going live.
  contact:
    name: SandPay Support
    url: https://docs.sandpay.dev
    email: support@sandpay.dev
  license:
    name: MIT
    identifier: MIT
servers:
  - url: https://api.sandpay.dev
    description: Production
  - url: https://api.sandbox.sandpay.dev
    description: Sandbox
security:
  - bearerAuth: []
tags:
  - name: Payments
    description: Create, retrieve, list, and refund simulated Mobile Money payments.
  - name: Disbursements
    description: Free-form payouts (merchant → an msisdn), not linked to a collection.
  - name: Meta
    description: API version + capability discovery for keeping integrations current.
  - name: Health
    description: Service health probe.
paths:
  /v1/payments:
    get:
      tags:
        - Payments
      summary: List payments
      description: Cursor-paginated list of payments for the authenticated organisation.
      operationId: listPayments
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 100
          description: Maximum number of payments to return (1–100).
        - in: query
          name: country
          schema:
            type: string
            minLength: 2
            maxLength: 2
          description: ISO-3166 2-letter country code filter (e.g. `CI`, `SN`).
        - in: query
          name: operator
          schema:
            type: string
            enum:
              - mtn
              - orange
              - moov
              - airtel
          description: Operator code filter.
        - in: query
          name: status
          schema:
            type: string
            enum:
              - SUCCESS
              - PIN_INVALID
              - INSUFFICIENT_FUNDS
              - TIMEOUT
              - ACCOUNT_BLOCKED
              - USER_CANCELLED
              - UNKNOWN_MSISDN
              - LIMIT_EXCEEDED
              - SERVICE_UNAVAILABLE
              - DUPLICATE_REFERENCE
              - PENDING
          description: Final payment status filter.
        - in: query
          name: type
          schema:
            $ref: '#/components/schemas/PaymentType'
          description: Transaction direction filter (collection / refund / disbursement).
        - in: query
          name: cursor
          schema:
            type: string
          description: Opaque pagination cursor returned by a previous call.
      responses:
        '200':
          description: Paginated list of payments.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentList'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    PaymentType:
      type: string
      enum:
        - collection
        - refund
        - disbursement
      description: |
        Transaction direction (B119): `collection` (pay-in, customer →
        merchant), `refund` (payout reversing a collection), or
        `disbursement` (free-form payout, merchant → an msisdn).
    PaymentList:
      type: object
      required:
        - data
        - nextCursor
        - hasMore
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Payment'
        nextCursor:
          type: string
          nullable: true
          description: Cursor to pass back as `cursor` to fetch the next page.
        hasMore:
          type: boolean
          description: True if there are more payments after this page.
    ErrorResponse:
      type: object
      required:
        - error
        - message
      properties:
        error:
          type: string
          description: >-
            Machine-readable error code (e.g. `invalid_body`, `unauthorized`,
            `quota_exceeded`).
          example: quota_exceeded
        message:
          type: string
          description: Human-readable description of what went wrong.
          example: Monthly simulation quota exceeded.
        detail:
          type: object
          nullable: true
          description: Optional structured detail, e.g. field validation errors.
          additionalProperties: true
    Payment:
      type: object
      required:
        - id
        - type
        - parentTxId
        - orderRef
        - orderUrl
        - isUrlProtected
        - amount
        - commission
        - netAmount
        - customerTotal
        - merchantAbsorptionPct
        - merchantShare
        - customerShare
        - commissionMode
        - currency
        - operator
        - country
        - msisdn
        - reference
        - status
        - latencyMs
        - createdAt
        - raw
      properties:
        id:
          type: string
          example: TX_8K3M9F
          description: Transaction id.
        type:
          $ref: '#/components/schemas/PaymentType'
        parentTxId:
          type: string
          nullable: true
          description: |
            For a `refund`, the id of the original collection it reverses.
            `null` for collections and disbursements (B119).
          example: null
        orderRef:
          type: string
          description: >
            App order id grouping related transactions (collection + refunds +

            recurring charges). Falls back to `reference` when not supplied
            (B126).
          example: ORDER-2026-A1
        orderUrl:
          type: string
          format: uri
          nullable: true
          description: >-
            Public URL to the order in the consuming app; `null` when not set
            (B126).
          example: null
        isUrlProtected:
          type: boolean
          description: |
            Whether `orderUrl` requires a specific permission on the consuming
            app to open (admin-only, not public). The dashboard shows a
            "protected URL" hint accordingly. Defaults to `false` (B130).
          example: false
        amount:
          type: integer
          minimum: 1
          description: |
            Gross amount in minor units the customer was debited (e.g. CFA
            centimes are not used; FCFA uses whole units).
          example: 25000
        commission:
          type: number
          minimum: 0
          description: |
            Operator commission kept from the gross `amount` (B112). Defaults
            to 1% (1.5% for Moov). `0` when no settlement / commission applies.
          example: 250
        netAmount:
          type: number
          minimum: 0
          description: |
            Net the merchant received. Reconcile your merchant ledger against
            this, not `amount`. In `customer` mode this equals `amount` (the
            merchant gets the full amount); in `merchant` mode it is
            `amount − commission`.
          example: 25000
        customerTotal:
          type: number
          minimum: 0
          description: |
            Total the client was charged (`netAmount + commission`). In
            `customer` mode this is `amount + commission`; in `merchant` mode
            it equals `amount` (B113).
          example: 25250
        merchantAbsorptionPct:
          type: integer
          nullable: true
          minimum: 0
          maximum: 100
          description: |
            Percentage (0–100) of the operator commission the merchant absorbed
            for this payment (B114). `100` = merchant absorbs the full fee
            (default); `0` = customer absorbs all. `null` for legacy rows.
          example: 100
        merchantShare:
          type: number
          minimum: 0
          description: |
            Portion of the commission borne by the merchant
            (`amount − netAmount`). B114.
          example: 250
        customerShare:
          type: number
          minimum: 0
          description: |
            Portion of the commission borne by the customer
            (`commission − merchantShare`). B114.
          example: 0
        commissionMode:
          type: string
          nullable: true
          enum:
            - customer
            - merchant
          description: |
            Who absorbed the operator commission for this payment (B113):
            `customer` (default) — client pays `amount + commission`, merchant
            receives the full `amount`; `merchant` — client pays `amount`,
            merchant receives `amount − commission`. `null` for legacy rows.
          example: customer
        currency:
          type: string
          minLength: 3
          maxLength: 4
          example: FCFA
          description: ISO-4217-style currency code (`FCFA`, `XOF`, `XAF`).
        operator:
          $ref: '#/components/schemas/PaymentOperator'
        country:
          type: string
          minLength: 2
          maxLength: 2
          example: CI
          description: ISO-3166 2-letter country code.
        msisdn:
          type: string
          pattern: ^\+[1-9][0-9]{6,14}$
          example: '+22507123456'
          description: E.164-formatted phone number.
        reference:
          type: string
          minLength: 1
          maxLength: 80
          example: ZANA-PAY-20260531221015-8E2884A47B1C
          description: >-
            Caller-supplied idempotency reference (unique per org). Recommended
            canonical shape `ORIGIN-OP-TS-CODE` (e.g.
            `ZANA-PAY-20260531221015-8E2884A47B1C`): ORIGIN = your app slug
            (`SP` for SandPay), OP ∈ PAY/REF/DIS/ABO, TS = compact UTC datetime
            `YYYYMMDDHHMMSS`, CODE = 12-char uppercase hex. Any string is
            accepted — this is a recommended convention, not enforced. Distinct
            from `order_ref`.
        scenario:
          allOf:
            - $ref: '#/components/schemas/PaymentScenario'
          nullable: true
          description: Scenario used to drive the outcome, if explicit.
        status:
          $ref: '#/components/schemas/PaymentStatus'
        latencyMs:
          type: integer
          minimum: 0
          description: Total time spent processing the payment, in milliseconds.
          example: 1240
        createdAt:
          type: string
          format: date-time
          description: ISO-8601 timestamp when the payment was created.
        raw:
          type: object
          nullable: true
          additionalProperties: true
          description: |
            Native operator response payload. In sandbox this is *synthesized*
            to mirror the real operator shape (MTN MoMo, Orange Money, Moov
            Africa, Airtel Money) — with `_simulated: true` set on the
            top level so consumers can detect sandbox payloads. In production
            this is the verbatim payload returned by the upstream operator
            API.

            This field lets you write integration code (e.g.
            `tx.raw.financialTransactionId` for MTN, `tx.raw.responseCode`
            for Moov) against sandbox transactions and have the same code
            work in production without changes.

            `null` for legacy rows persisted before raw passthrough shipped.
          example:
            _simulated: true
            amount: '25000'
            currency: FCFA
            externalId: ORDER-2026-A1
            payer:
              partyIdType: MSISDN
              partyId: '22507123456'
            payerMessage: Payment
            payeeNote: ORDER-2026-A1
            status: SUCCESSFUL
            financialTransactionId: SIM_A1B2C3D4
    PaymentOperator:
      type: string
      enum:
        - mtn
        - orange
        - moov
        - airtel
      description: Mobile Money operator code.
    PaymentScenario:
      type: string
      enum:
        - success
        - pin_invalid
        - low_balance
        - timeout
        - blocked
        - cancelled
        - unknown_msisdn
        - limit_exceeded
        - maintenance
        - duplicate
      description: Scenario tag that drives the simulated outcome.
    PaymentStatus:
      type: string
      enum:
        - SUCCESS
        - PIN_INVALID
        - INSUFFICIENT_FUNDS
        - TIMEOUT
        - ACCOUNT_BLOCKED
        - USER_CANCELLED
        - UNKNOWN_MSISDN
        - LIMIT_EXCEEDED
        - SERVICE_UNAVAILABLE
        - DUPLICATE_REFERENCE
        - PENDING
      description: Final settlement status.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key in format `sp_sk_test_...` or `sp_sk_live_...`.

````