Skip to content

Pay-In API Specification

Owner Classification Review Date Status
CDO Office Internal April 2027 Active
openapi: 3.1.0
info:
  title: Simpaisa Pay-In API
  version: 3.0.0
  description: |
    Simpaisa Pay-In API enables merchants to collect payments from customers across
    Pakistan, Bangladesh, Nepal, and Iraq. Supporting 270M+ transactions
    and over $1B in processed volume, the platform provides secure, reliable
    payment collection with tokenisation, OTP verification and real-time
    webhook notifications.

    All requests must be signed using RSA-SHA256. Card data fields must be
    AES-encrypted before transmission.
  contact:
    name: Simpaisa Platform Engineering
    url: https://www.simpaisa.com
    email: [email protected]
  license:
    name: Proprietary
    url: https://www.simpaisa.com/terms
  x-logo:
    url: https://www.simpaisa.com/logo.png

servers:
  - url: https://api.simpaisa.com
    description: Production
  - url: https://sandbox.simpaisa.com
    description: Sandbox

security:
  - rsaSha256Signature: []

paths:
  /v3/payin/{merchantId}/transactions/initiate:
    post:
      operationId: initiatePayIn
      summary: Initiate a pay-in transaction
      description: |
        Creates a new pay-in transaction. The response includes a transaction
        identifier and, where applicable, an OTP challenge reference for
        customer verification.
      tags:
        - Transactions
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PayInInitiateRequest'
      responses:
        '201':
          description: Transaction initiated successfully
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PayInInitiateResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
      callbacks:
        transactionStatusUpdate:
          '{$request.body#/callbackUrl}':
            post:
              summary: Transaction status webhook
              description: Sent when the transaction status changes.
              requestBody:
                required: true
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/WebhookPayload'
              responses:
                '200':
                  description: Webhook acknowledged

  /v3/payin/{merchantId}/transactions/{transactionId}/verify:
    post:
      operationId: verifyPayInOTP
      summary: Verify OTP for a pay-in transaction
      description: Submits the OTP received by the customer to complete verification.
      tags:
        - Transactions
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/TransactionIdPath'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OTPVerifyRequest'
      responses:
        '200':
          description: OTP verified successfully
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/payin/{merchantId}/transactions/{transactionId}:
    get:
      operationId: getPayInTransaction
      summary: Retrieve a pay-in transaction
      description: Returns the current state of a pay-in transaction.
      tags:
        - Transactions
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/TransactionIdPath'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      responses:
        '200':
          description: Transaction retrieved
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionResponse'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/payin/{merchantId}/transactions/{transactionId}/refund:
    post:
      operationId: refundPayIn
      summary: Refund a pay-in transaction
      description: |
        Initiates a full or partial refund for a completed pay-in transaction.
        Partial refunds are supported where the refund amount is less than the
        original transaction amount.
      tags:
        - Transactions
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/TransactionIdPath'
        - $ref: '#/components/parameters/IdempotencyKey'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefundRequest'
      responses:
        '200':
          description: Refund initiated
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/payin/{merchantId}/tokens:
    get:
      operationId: listPaymentTokens
      summary: List saved payment tokens
      description: Returns all active payment tokens for the merchant's customers.
      tags:
        - Tokens
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      responses:
        '200':
          description: Token list retrieved
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                type: object
                properties:
                  tokens:
                    type: array
                    items:
                      $ref: '#/components/schemas/PaymentToken'
                  traceId:
                    type: string
                required:
                  - tokens
                  - traceId
        '401':
          $ref: '#/components/responses/Unauthorised'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/payin/{merchantId}/tokens/{tokenId}:
    delete:
      operationId: deletePaymentToken
      summary: Delete a payment token
      description: Permanently removes a saved payment token.
      tags:
        - Tokens
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - name: tokenId
          in: path
          required: true
          description: Unique token identifier
          schema:
            type: string
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      responses:
        '204':
          description: Token deleted
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
        '401':
          $ref: '#/components/responses/Unauthorised'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v3/payin/{merchantId}/operators:
    get:
      operationId: listOperators
      summary: List available payment operators
      description: |
        Returns the list of payment operators (mobile wallets, bank channels, etc.)
        available for the merchant in their configured corridors.
      tags:
        - Operators
      parameters:
        - $ref: '#/components/parameters/MerchantIdPath'
        - $ref: '#/components/parameters/XMerchantId'
        - $ref: '#/components/parameters/XTimestamp'
        - $ref: '#/components/parameters/XSignature'
      responses:
        '200':
          description: Operator list retrieved
          headers:
            X-Trace-Id:
              $ref: '#/components/headers/XTraceId'
            X-RateLimit-Limit:
              $ref: '#/components/headers/XRateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/XRateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/XRateLimitReset'
          content:
            application/json:
              schema:
                type: object
                properties:
                  operators:
                    type: array
                    items:
                      $ref: '#/components/schemas/Operator'
                  traceId:
                    type: string
                required:
                  - operators
                  - traceId
        '401':
          $ref: '#/components/responses/Unauthorised'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'

components:
  securitySchemes:
    rsaSha256Signature:
      type: apiKey
      in: header
      name: X-Signature
      description: |
        RSA-SHA256 signature computed over the canonical request string.
        The merchant signs the request payload concatenated with the timestamp
        using their RSA private key. Simpaisa verifies the signature using the
        merchant's registered public key.

  parameters:
    MerchantIdPath:
      name: merchantId
      in: path
      required: true
      description: Unique merchant identifier issued during onboarding
      schema:
        type: string
        example: MCH-00123

    TransactionIdPath:
      name: transactionId
      in: path
      required: true
      description: Unique transaction identifier returned at initiation
      schema:
        type: string
        format: uuid

    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      description: |
        Client-generated unique key to ensure idempotent request processing.
        Duplicate requests with the same key within 24 hours return the
        original response without reprocessing.
      schema:
        type: string
        format: uuid

    XMerchantId:
      name: X-Merchant-Id
      in: header
      required: true
      description: Merchant identifier for request authentication
      schema:
        type: string

    XTimestamp:
      name: X-Timestamp
      in: header
      required: true
      description: ISO 8601 timestamp of the request, used in signature computation
      schema:
        type: string
        format: date-time

    XSignature:
      name: X-Signature
      in: header
      required: true
      description: RSA-SHA256 signature of the canonical request string
      schema:
        type: string

  headers:
    XTraceId:
      description: Unique trace identifier for request tracking and debugging
      schema:
        type: string
        format: uuid
    XRateLimitLimit:
      description: Maximum number of requests permitted per window
      schema:
        type: integer
    XRateLimitRemaining:
      description: Number of requests remaining in the current window
      schema:
        type: integer
    XRateLimitReset:
      description: Unix epoch timestamp when the rate limit window resets
      schema:
        type: integer

  schemas:
    PayInInitiateRequest:
      type: object
      required:
        - amount
        - currency
        - customerMsisdn
        - operatorId
        - merchantTransactionId
      properties:
        amount:
          type: number
          format: double
          description: Transaction amount in the specified currency
          example: 1500.00
        currency:
          type: string
          pattern: '^[A-Z]{3}$'
          description: ISO 4217 currency code
          example: PKR
        customerMsisdn:
          type: string
          description: Customer mobile number in E.164 format
          example: '+923001234567'
        operatorId:
          type: string
          description: Payment operator identifier from the operators endpoint
          example: jazz-cash
        merchantTransactionId:
          type: string
          description: Merchant's own unique reference for this transaction
          maxLength: 64
          example: ORD-2026-00456
        callbackUrl:
          type: string
          format: uri
          description: URL for asynchronous webhook notifications
          example: https://merchant.example.com/webhooks/simpaisa
        tokenId:
          type: string
          description: Saved payment token for returning customers
        saveToken:
          type: boolean
          description: Whether to tokenise this payment method for future use
          default: false
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Arbitrary key-value pairs stored with the transaction
          example:
            orderId: '12345'
            channel: mobile-app

    PayInInitiateResponse:
      type: object
      required:
        - transactionId
        - merchantTransactionId
        - status
        - traceId
      properties:
        transactionId:
          type: string
          format: uuid
          description: Simpaisa-assigned unique transaction identifier
        merchantTransactionId:
          type: string
          description: Echo of the merchant's reference
        status:
          type: string
          enum:
            - INITIATED
            - OTP_REQUIRED
            - PROCESSING
          description: Current transaction status
        otpReference:
          type: string
          description: OTP challenge reference, present when status is OTP_REQUIRED
        redirectUrl:
          type: string
          format: uri
          description: URL for customer redirection (operator-hosted flows)
        expiresAt:
          type: string
          format: date-time
          description: Timestamp after which the transaction will expire
        traceId:
          type: string
          format: uuid
          description: Request trace identifier

    OTPVerifyRequest:
      type: object
      required:
        - otp
        - otpReference
      properties:
        otp:
          type: string
          description: One-time password received by the customer
          minLength: 4
          maxLength: 8
          example: '123456'
        otpReference:
          type: string
          description: OTP reference from the initiation response

    TransactionResponse:
      type: object
      required:
        - transactionId
        - merchantTransactionId
        - status
        - amount
        - currency
        - traceId
      properties:
        transactionId:
          type: string
          format: uuid
        merchantTransactionId:
          type: string
        status:
          type: string
          enum:
            - INITIATED
            - OTP_REQUIRED
            - PROCESSING
            - COMPLETED
            - FAILED
            - REFUNDED
            - PARTIALLY_REFUNDED
            - EXPIRED
        amount:
          type: number
          format: double
        currency:
          type: string
        operatorId:
          type: string
        operatorTransactionId:
          type: string
          description: Transaction reference from the payment operator
        refundedAmount:
          type: number
          format: double
          description: Total amount refunded, if applicable
        tokenId:
          type: string
          description: Payment token created if saveToken was true
        completedAt:
          type: string
          format: date-time
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties:
            type: string
        traceId:
          type: string
          format: uuid

    RefundRequest:
      type: object
      required:
        - amount
        - reason
      properties:
        amount:
          type: number
          format: double
          description: Refund amount; must not exceed the original transaction amount
          example: 500.00
        reason:
          type: string
          description: Reason for the refund
          maxLength: 256
          example: Customer requested cancellation

    PaymentToken:
      type: object
      required:
        - tokenId
        - maskedAccount
        - operatorId
        - createdAt
      properties:
        tokenId:
          type: string
          description: Unique token identifier
        maskedAccount:
          type: string
          description: Masked customer account or mobile number
          example: '+92300***4567'
        operatorId:
          type: string
          description: Payment operator associated with this token
        label:
          type: string
          description: Customer-friendly label
        createdAt:
          type: string
          format: date-time
        lastUsedAt:
          type: string
          format: date-time

    Operator:
      type: object
      required:
        - operatorId
        - name
        - country
        - status
      properties:
        operatorId:
          type: string
          example: jazz-cash
        name:
          type: string
          example: JazzCash
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code
          example: PK
        type:
          type: string
          enum:
            - MOBILE_WALLET
            - BANK_ACCOUNT
            - CARD
            - OTC
          description: Operator channel type
        currencies:
          type: array
          items:
            type: string
          description: Supported ISO 4217 currency codes
          example:
            - PKR
        minAmount:
          type: number
          format: double
          description: Minimum transaction amount
        maxAmount:
          type: number
          format: double
          description: Maximum transaction amount
        status:
          type: string
          enum:
            - ACTIVE
            - DEGRADED
            - INACTIVE
          description: Current operator availability

    WebhookPayload:
      type: object
      required:
        - event
        - transactionId
        - merchantTransactionId
        - status
        - timestamp
        - signature
      properties:
        event:
          type: string
          enum:
            - TRANSACTION_COMPLETED
            - TRANSACTION_FAILED
            - TRANSACTION_EXPIRED
            - REFUND_COMPLETED
            - REFUND_FAILED
          description: Event type
        transactionId:
          type: string
          format: uuid
        merchantTransactionId:
          type: string
        status:
          type: string
        amount:
          type: number
          format: double
        currency:
          type: string
        operatorTransactionId:
          type: string
        timestamp:
          type: string
          format: date-time
        signature:
          type: string
          description: RSA-SHA256 signature for webhook verification
        metadata:
          type: object
          additionalProperties:
            type: string

    ErrorResponse:
      type: object
      required:
        - error
        - traceId
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code
              example: INVALID_AMOUNT
            message:
              type: string
              description: Human-readable error description
              example: The amount must be greater than zero.
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                    description: Field that caused the error
                  reason:
                    type: string
                    description: Specific validation failure
              description: Field-level validation errors
        traceId:
          type: string
          format: uuid
          description: Request trace identifier for debugging

  responses:
    BadRequest:
      description: Invalid request parameters
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    Unauthorised:
      description: Authentication failed — invalid or missing signature
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    NotFound:
      description: Resource not found
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    Conflict:
      description: Duplicate Idempotency-Key with different request body
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    UnprocessableEntity:
      description: Request understood but cannot be processed
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    TooManyRequests:
      description: Rate limit exceeded
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
        Retry-After:
          description: Seconds to wait before retrying
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

    InternalServerError:
      description: Unexpected server error
      headers:
        X-Trace-Id:
          $ref: '#/components/headers/XTraceId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'

tags:
  - name: Transactions
    description: Pay-in transaction lifecycle operations
  - name: Tokens
    description: Saved payment token management
  - name: Operators
    description: Payment operator discovery