Miden Open API Docs

openapi: 3.0.3

info:

title: Miden Kenya – Crypto Payment Gateway API

version: 1.1.0

description: |

Accept crypto (USDT, USDC, BTC, ETH), auto-convert, and settle to Kenyan rails: M-Pesa, Airtel Money, T-Kash, and PesaLink.

Custody, MPC signing and hot/cold workflows are handled via Fireblocks. All local payouts are in KES.

Key improvements in v1.1.0:

- Token lifecycle clarified (expiry, rotation), optional scopes

- Idempotency header documented and enforced on POST creates

- On-chain confirmation policy per asset

- Rate limits + headers

- Detailed error catalog with machine codes

- Webhook signature (HMAC) + retry policy

- Sandbox test data and rail-specific examples

servers:

- url: https://api.midenkenya.com/v1

description: Production

- url: https://sandbox.api.midenkenya.com/v1

description: Sandbox

security:

- bearerAuth: []

- oauth2ClientCreds: []

externalDocs:

description: Developer portal

url: https://midenkenya.com/docs

x-idempotency:

header: Idempotency-Key

description: Unique per creating request to guarantee exactly-once semantics. Required on all POST create endpoints.

tags:

- name: Auth

- name: Payments – USDT

- name: Payments – USDC

- name: Payments – BTC

- name: Payments – ETH

- name: Settlements – M-Pesa

- name: Settlements – Airtel Money

- name: Settlements – T-Kash

- name: Settlements – PesaLink

- name: Refunds

- name: Directory

- name: Webhooks

paths:

/auth/token:

post:

tags: [Auth]

summary: Obtain OAuth2 access token (Client Credentials)

description: |

Exchange `client_id` and `client_secret` for a short-lived JWT. Default expiry is 3600 seconds.

Rotate client secrets at least quarterly. Optional scopes are reserved for future RBAC.

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/TokenRequest' }

example:

client_id: your_client_id

client_secret: your_client_secret

grant_type: client_credentials

responses:

'200':

description: Access token

headers:

X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }

X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }

content:

application/json:

schema: { $ref: '#/components/schemas/TokenResponse' }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

############################

# PAYMENTS (per asset)

############################

/payments/usdt:

post:

tags: [Payments – USDT]

summary: Create USDT payment (quote + deposit address)

description: |

Supported networks: ETH (ERC-20), TRX (TRC-20). Quote expires at `quote.expires_at`.

On-chain confirmation policy: 12 blocks (ETH), 20 blocks (TRX) before status becomes CONFIRMED.

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/PaymentCreateUSDT' }

example:

network: ETH

amount_fiat: 1000

currency: KES

customer_reference: ORDER-123

callback_url: https://merchant.example/callback

responses:

'201':

description: Payment created

headers:

X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }

X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }

content:

application/json:

schema: { $ref: '#/components/schemas/Payment' }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/payments/usdc:

post:

tags: [Payments – USDC]

summary: Create USDC payment (quote + deposit address)

description: |

Supported network: ETH (ERC-20). Confirmation policy: 12 blocks before CONFIRMED.

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/PaymentCreateUSDC' }

example:

network: ETH

amount_fiat: 32500

currency: KES

customer_reference: SO-98234

callback_url: https://merchant.example/callback

responses:

'201':

description: Payment created

headers:

X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }

X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }

content:

application/json:

schema: { $ref: '#/components/schemas/Payment' }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/payments/btc:

post:

tags: [Payments – BTC]

summary: Create BTC payment (quote + deposit address)

description: |

Supported network: BTC (native). Confirmation policy: 1 block for low value, 3 blocks standard before CONFIRMED.

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/PaymentCreateBTC' }

example:

amount_fiat: 5000

currency: KES

customer_reference: INV-2025-045

callback_url: https://merchant.example/callback

responses:

'201':

description: Payment created

headers:

X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }

X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }

content:

application/json:

schema: { $ref: '#/components/schemas/Payment' }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/payments/eth:

post:

tags: [Payments – ETH]

summary: Create ETH payment (quote + deposit address)

description: |

Supported network: ETH (native). Confirmation policy: 12 blocks before CONFIRMED.

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/PaymentCreateETH' }

example:

amount_fiat: 12000

currency: KES

customer_reference: ORD-8842

callback_url: https://merchant.example/callback

responses:

'201':

description: Payment created

headers:

X-RateLimit-Limit: { $ref: '#/components/headers/X-RateLimit-Limit' }

X-RateLimit-Remaining: { $ref: '#/components/headers/X-RateLimit-Remaining' }

content:

application/json:

schema: { $ref: '#/components/schemas/Payment' }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/payments/{payment_id}:

get:

tags: [Payments – USDT, Payments – USDC, Payments – BTC, Payments – ETH]

summary: Retrieve a payment by ID

security: [{ bearerAuth: [] }]

parameters:

- name: payment_id

in: path

required: true

schema: { type: string }

responses:

'200':

description: Payment

content:

application/json:

schema: { $ref: '#/components/schemas/Payment' }

'404': { $ref: '#/components/responses/NotFound' }

############################

# SETTLEMENTS (per rail)

############################

/settlements/mpesa:

post:

tags: ['Settlements – M-Pesa']

summary: Create M-Pesa settlement

description: |

Payout to Safaricom M-Pesa wallet. Recipient MSISDN format: `2547XXXXXXXX`.

Provider: https://www.safaricom.co.ke/m-pesa

Limits vary by recipient tier; large payouts should be split automatically.

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/MpesaSettlementRequest' }

example:

payment_id: pay_01J8E9W4C2XJ9

channel: MPESA

recipient_number: '254712345678'

amount: 1000

narration: 'Invoice #1234'

responses:

'201': { description: Settlement created, content: { application/json: { schema: { $ref: '#/components/schemas/MobileMoneySettlementResponse' } } } }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/settlements/airtel:

post:

tags: ['Settlements – Airtel Money']

summary: Create Airtel Money settlement

description: |

Payout to Airtel Money wallet. Recipient format: `2547XXXXXXXX`.

Provider: https://www.airtelmoney.ke/

Airtel may enforce tighter velocity limits; batch intelligently.

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/AirtelSettlementRequest' }

example:

payment_id: pay_01J8E9W4C2XJ9

channel: AIRTEL

recipient_number: '254733000000'

amount: 1000

narration: 'Payout AUG-25'

responses:

'201': { description: Settlement created, content: { application/json: { schema: { $ref: '#/components/schemas/MobileMoneySettlementResponse' } } } }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/settlements/tkash:

post:

tags: ['Settlements – T-Kash']

summary: Create T-Kash settlement

description: |

Payout to Telkom T-Kash. Recipient format: `2547XXXXXXXX`.

Provider: https://telkom.co.ke/t-kash/

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/TkashSettlementRequest' }

example:

payment_id: pay_01J8E9W4C2XJ9

channel: TKASH

recipient_number: '254770000000'

amount: 1500

responses:

'201': { description: Settlement created, content: { application/json: { schema: { $ref: '#/components/schemas/MobileMoneySettlementResponse' } } } }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/settlements/pesalink:

post:

tags: ['Settlements – PesaLink']

summary: Create PesaLink settlement (bank-to-bank)

description: |

Interbank settlement via IPSL through sponsor bank/aggregator.

Provider: https://www.pesalink.co.ke/

Always capture and persist `rrn` for reconciliation.

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/PesalinkSettlementRequest' }

example:

payment_id: pay_01J8E9W4C2XJ9

channel: PESALINK

bank_code: KCBL

account_number: '1234567890'

account_name: 'ACME LTD'

amount: 100000

narration: 'Invoice 2025-0198'

reference: 'INV-2025-0198'

responses:

'201': { description: Settlement created, content: { application/json: { schema: { $ref: '#/components/schemas/PesalinkSettlementResponse' } } } }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/settlements/{settlement_id}:

get:

tags: ['Settlements – M-Pesa', 'Settlements – Airtel Money', 'Settlements – T-Kash', 'Settlements – PesaLink']

summary: Get settlement status

security: [{ bearerAuth: [] }]

parameters:

- name: settlement_id

in: path

required: true

schema: { type: string }

responses:

'200':

description: Settlement status

content:

application/json:

schema: { oneOf: [ { $ref: '#/components/schemas/MobileMoneySettlementStatus' }, { $ref: '#/components/schemas/PesalinkSettlementStatus' } ] }

'404': { $ref: '#/components/responses/NotFound' }

############################

# DIRECTORY & REFUNDS

############################

/directory/pesalink/banks:

get:

tags: [Directory]

summary: List PesaLink participant banks

security: [{ bearerAuth: [] }]

responses:

'200':

description: Bank directory

content:

application/json:

schema:

type: array

items: { $ref: '#/components/schemas/PesalinkBank' }

/refunds:

post:

tags: [Refunds]

summary: Create a refund

description: Refund on-chain or to a local rail.

security: [{ bearerAuth: [] }]

parameters:

- $ref: '#/components/parameters/IdempotencyKey'

requestBody:

required: true

content:

application/json:

schema: { $ref: '#/components/schemas/RefundCreateRequest' }

examples:

mpesa:

value:

payment_id: pay_01J8E9W4C2XJ9

amount: 500

method: MPESA

destination: { recipient_number: '254712345678' }

reason: Customer cancellation

onchain:

value:

payment_id: pay_01J8E9W4C2XJ9

amount: 7.83

method: ONCHAIN

destination: { asset: USDT, network: ETH, address: '0xabc...' }

reason: Partial refund

responses:

'201':

description: Refund created

content:

application/json:

schema: { $ref: '#/components/schemas/Refund' }

'400': { $ref: '#/components/responses/BadRequest' }

'401': { $ref: '#/components/responses/Unauthorized' }

'409': { $ref: '#/components/responses/Conflict' }

/refunds/{refund_id}:

get:

tags: [Refunds]

summary: Retrieve a refund

security: [{ bearerAuth: [] }]

parameters:

- name: refund_id

in: path

required: true

schema: { type: string }

responses:

'200':

description: Refund

content:

application/json:

schema: { $ref: '#/components/schemas/Refund' }

'404': { $ref: '#/components/responses/NotFound' }

############################

# WEBHOOKS (outbound)

############################

/webhooks/test:

post:

tags: [Webhooks]

summary: Trigger a test webhook to your endpoint (sandbox only)

security: [{ bearerAuth: [] }]

requestBody:

required: true

content:

application/json:

schema:

type: object

properties:

url: { type: string, format: uri }

event: { type: string, enum: [payment.confirmed, settlement.success, settlement.failed, settlement.reversed] }

responses:

'202': { description: Test webhook scheduled }

components:

securitySchemes:

bearerAuth:

type: http

scheme: bearer

bearerFormat: JWT

oauth2ClientCreds:

type: oauth2

flows:

clientCredentials:

tokenUrl: https://api.midenkenya.com/v1/auth/token

scopes: {}

headers:

X-RateLimit-Limit:

description: Requests allowed in the current window

schema: { type: integer }

X-RateLimit-Remaining:

description: Requests remaining in the current window

schema: { type: integer }

Retry-After:

description: Seconds to wait before retrying (returned on 429)

schema: { type: integer }

parameters:

IdempotencyKey:

name: Idempotency-Key

in: header

required: true

description: Unique key to ensure exactly-once processing of create requests

schema: { type: string }

responses:

BadRequest:

description: Bad request

content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }

Unauthorized:

description: Unauthorized or invalid token

content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }

NotFound:

description: Resource not found

content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }

Conflict:

description: Duplicate or conflicting request

content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } }

schemas:

############################

# AUTH

############################

TokenRequest:

type: object

required: [client_id, client_secret, grant_type]

properties:

client_id: { type: string }

client_secret: { type: string }

grant_type:

type: string

enum: [client_credentials]

TokenResponse:

type: object

properties:

access_token: { type: string }

token_type: { type: string, example: Bearer }

expires_in: { type: integer, example: 3600 }

############################

# PAYMENTS

############################

PaymentCreateUSDT:

type: object

required: [network, amount_fiat, currency]

properties:

network:

type: string

enum: [ETH, TRX]

amount_fiat: { type: number, format: double, minimum: 1 }

currency:

type: string

enum: [KES]

customer_reference: { type: string }

callback_url: { type: string, format: uri }

metadata:

type: object

additionalProperties: { type: string }

PaymentCreateUSDC:

type: object

required: [network, amount_fiat, currency]

properties:

network:

type: string

enum: [ETH]

amount_fiat: { type: number, format: double, minimum: 1 }

currency:

type: string

enum: [KES]

customer_reference: { type: string }

callback_url: { type: string, format: uri }

metadata:

type: object

additionalProperties: { type: string }

PaymentCreateBTC:

type: object

required: [amount_fiat, currency]

properties:

amount_fiat: { type: number, format: double, minimum: 1 }

currency:

type: string

enum: [KES]

customer_reference: { type: string }

callback_url: { type: string, format: uri }

metadata:

type: object

additionalProperties: { type: string }

PaymentCreateETH:

type: object

required: [amount_fiat, currency]

properties:

amount_fiat: { type: number, format: double, minimum: 1 }

currency:

type: string

enum: [KES]

customer_reference: { type: string }

callback_url: { type: string, format: uri }

metadata:

type: object

additionalProperties: { type: string }

Payment:

type: object

properties:

payment_id: { type: string }

asset: { type: string, enum: [USDT, USDC, BTC, ETH] }

network: { type: string, nullable: true }

status: { type: string, enum: [PENDING, CONFIRMED, EXPIRED, CANCELLED] }

quote:

type: object

properties:

amount_fiat: { type: number }

currency: { type: string, enum: [KES] }

expected_asset_amount: { type: string }

rate: { type: string, description: Fiat-to-asset rate used }

expires_at: { type: string, format: date-time }

deposit:

type: object

properties:

address: { type: string }

memo: { type: string, nullable: true }

qr: { type: string, description: Base64 PNG data URL }

amount_received: { type: string }

fiat_equivalent: { type: number }

customer_reference: { type: string, nullable: true }

created_at: { type: string, format: date-time }

confirmed_at: { type: string, format: date-time, nullable: true }

metadata:

type: object

additionalProperties: { type: string }

############################

# SETTLEMENTS

############################

MpesaSettlementRequest:

type: object

required: [payment_id, channel, recipient_number, amount]

properties:

payment_id: { type: string }

channel: { type: string, enum: [MPESA] }

recipient_number:

type: string

description: MSISDN in international format, e.g., 2547XXXXXXXX

pattern: '^2547\d{8}$'

amount: { type: number, format: double, minimum: 1 }

narration: { type: string, maxLength: 40 }

metadata:

type: object

additionalProperties: { type: string }

AirtelSettlementRequest:

type: object

required: [payment_id, channel, recipient_number, amount]

properties:

payment_id: { type: string }

channel: { type: string, enum: [AIRTEL] }

recipient_number:

type: string

description: MSISDN in international format, e.g., 2547XXXXXXXX

pattern: '^2547\d{8}$'

amount: { type: number, format: double, minimum: 1 }

narration: { type: string, maxLength: 40 }

metadata:

type: object

additionalProperties: { type: string }

TkashSettlementRequest:

type: object

required: [payment_id, channel, recipient_number, amount]

properties:

payment_id: { type: string }

channel: { type: string, enum: [TKASH] }

recipient_number:

type: string

description: MSISDN in international format

pattern: '^2547\d{8}$'

amount: { type: number, format: double, minimum: 1 }

narration: { type: string, maxLength: 40 }

metadata:

type: object

additionalProperties: { type: string }

PesalinkSettlementRequest:

type: object

required: [payment_id, channel, bank_code, account_number, account_name, amount]

properties:

payment_id: { type: string }

channel: { type: string, enum: [PESALINK] }

bank_code: { type: string, example: KCBL }

account_number: { type: string }

account_name: { type: string }

amount: { type: number, format: double, minimum: 1 }

narration: { type: string, maxLength: 40 }

reference: { type: string, description: Merchant reference for reconciliation }

proxy_type:

type: string

enum: [PHONE, ACCOUNT]

nullable: true

proxy_value: { type: string, nullable: true }

metadata:

type: object

additionalProperties: { type: string }

MobileMoneySettlementResponse:

type: object

properties:

settlement_id: { type: string }

channel: { type: string, enum: [MPESA, AIRTEL, TKASH] }

status: { type: string, enum: [PROCESSING, SUCCESS, FAILED, REVERSED] }

amount: { type: number }

currency: { type: string, enum: [KES] }

beneficiary:

type: object

properties:

recipient_number: { type: string }

provider_reference: { type: string, nullable: true }

created_at: { type: string, format: date-time }

completed_at: { type: string, format: date-time, nullable: true }

MobileMoneySettlementStatus:

type: object

properties:

settlement_id: { type: string }

status: { type: string, enum: [PROCESSING, SUCCESS, FAILED, REVERSED] }

provider_reference: { type: string, nullable: true }

failure_reason: { type: string, nullable: true }

completed_at: { type: string, format: date-time, nullable: true }

PesalinkSettlementResponse:

type: object

properties:

settlement_id: { type: string }

channel: { type: string, enum: [PESALINK] }

status: { type: string, enum: [PROCESSING, SUCCESS, FAILED, REVERSED] }

amount: { type: number }

currency: { type: string, enum: [KES] }

beneficiary:

type: object

properties:

bank_code: { type: string }

account_number: { type: string }

account_name: { type: string }

rrn: { type: string, description: IPSL Retrieval Reference Number }

reference: { type: string, nullable: true }

created_at: { type: string, format: date-time }

completed_at: { type: string, format: date-time, nullable: true }

PesalinkSettlementStatus:

type: object

properties:

settlement_id: { type: string }

status: { type: string, enum: [PROCESSING, SUCCESS, FAILED, REVERSED] }

rrn: { type: string, nullable: true }

bank_response_code: { type: string, nullable: true }

failure_reason: { type: string, nullable: true }

completed_at: { type: string, format: date-time, nullable: true }

PesalinkBank:

type: object

properties:

bank_code: { type: string }

bank_name: { type: string }

############################

# REFUNDS & ERROR

############################

RefundCreateRequest:

type: object

required: [payment_id, amount, method]

properties:

payment_id: { type: string }

amount: { type: number, format: double, minimum: 1 }

method:

type: string

enum: [ONCHAIN, MPESA, AIRTEL, TKASH, PESALINK]

destination:

type: object

description: Destination details required for local-rail refunds

additionalProperties: true

reason: { type: string }

Refund:

type: object

properties:

refund_id: { type: string }

payment_id: { type: string }

amount: { type: number }

method: { type: string }

status: { type: string, enum: [PENDING, PROCESSING, SUCCESS, FAILED] }

created_at: { type: string, format: date-time }

completed_at: { type: string, format: date-time, nullable: true }

Error:

type: object

properties:

code:

type: string

enum:

- INVALID_REQUEST

- UNAUTHORIZED

- NOT_FOUND

- CONFLICT

- RATE_LIMITED

- PROVIDER_DOWN

- INVALID_MSISDN

- LIMIT_EXCEEDED

- NAME_MISMATCH

- INSUFFICIENT_LIQUIDITY

- RISK_BLOCKED

- INTERNAL_ERROR

message: { type: string }

trace_id: { type: string }

details:

type: object

additionalProperties: true

x-webhooks:

payment.confirmed:

post:

tags: [Webhooks]

summary: Payment confirmed on-chain

description: |

Sent when crypto funds meet the confirmation policy and are locked for settlement.

Verify signatures using HMAC SHA-256 over raw body with your webhook secret provided in the dashboard.

Header: `X-Miden-Signature: t=timestamp,v1=hexdigest`.

requestBody:

required: true

content:

application/json:

schema:

type: object

properties:

event: { type: string, example: payment.confirmed }

data:

type: object

properties:

payment_id: { type: string }

asset: { type: string, enum: [USDT, USDC, BTC, ETH] }

network: { type: string }

amount_received: { type: string }

fiat_equivalent: { type: number }

confirmed_at: { type: string, format: date-time }

responses:

'200': { description: Acknowledge }

settlement.success:

post:

tags: [Webhooks]

summary: Settlement succeeded

description: Retries with exponential backoff up to 24 hours if your endpoint returns non-2xx.

requestBody:

required: true

content:

application/json:

schema:

type: object

properties:

event: { type: string, example: settlement.success }

data:

type: object

properties:

settlement_id: { type: string }

channel: { type: string, enum: [MPESA, AIRTEL, TKASH, PESALINK] }

amount: { type: number }

currency: { type: string, enum: [KES] }

provider_reference: { type: string, nullable: true }

rrn: { type: string, nullable: true }

timestamp: { type: string, format: date-time }

responses:

'200': { description: Acknowledge }

settlement.failed:

post:

tags: [Webhooks]

summary: Settlement failed

requestBody:

required: true

content:

application/json:

schema:

type: object

properties:

event: { type: string, example: settlement.failed }

data:

type: object

properties:

settlement_id: { type: string }

channel: { type: string }

amount: { type: number }

failure_reason: { type: string }

timestamp: { type: string, format: date-time }

responses:

'200': { description: Acknowledge }

settlement.reversed:

post:

tags: [Webhooks]

summary: Settlement reversed

requestBody:

required: true

content:

application/json:

schema:

type: object

properties:

event: { type: string, example: settlement.reversed }

data:

type: object

properties:

settlement_id: { type: string }

channel: { type: string }

amount: { type: number }

reason: { type: string }

timestamp: { type: string, format: date-time }

responses:

'200': { description: Acknowledge }

x-sandbox:

mpesa:

test_msisdn: '254700000000'

airtel:

test_msisdn: '254733000000'

tkash:

test_msisdn: '254770000000'

pesalink:

demo_bank_code: 'DEMO'

down_bank_code: 'DOWN'

name_mismatch_amount_trigger: 1234