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