Payments API
Endpoints
| Method | Path | Scope | Description |
|---|---|---|---|
POST |
/v1/payments |
payments:write |
Create a payment |
GET |
/v1/payments |
payments:read |
List & filter transactions |
GET |
/v1/payments/{id} |
payments:read |
Get payment status |
POST |
/v1/payments/{id}/capture |
payments:write |
Capture PayPal order |
POST |
/v1/payments/mpesa/check-status/{code} |
payments:read |
Check M-Pesa transaction |
POST |
/v1/payments/mpesa/reconcile |
payments:write |
Pull missed M-Pesa transactions |
POST |
/v1/payments/mpesa/register-pull |
payments:write |
Register for Pull API |
POST |
/v1/payments/cards |
payments:write |
Save a card |
GET |
/v1/payments/cards |
payments:read |
List saved cards |
DELETE |
/v1/payments/cards/{id} |
payments:write |
Delete a saved card |
Response Envelope
All responses follow this format:
Payment Object
{
"id": "a1b2c3d4-...",
"merchant_id": "m1m2m3-...",
"amount": "50.00",
"currency": "USD",
"provider": "stripe",
"status": "completed",
"provider_ref": "pi_3abc...",
"idempotency_key": "order-123",
"created_at": "2025-01-01T12:00:00Z"
}
Payment Statuses
stateDiagram-v2
[*] --> pending: Payment created
pending --> completed: Provider confirms success
pending --> failed: Provider declines / error
completed --> refunded: Stripe/PayPal refund
completed --> reversed: M-Pesa reversal
failed --> [*]
refunded --> [*]
reversed --> [*]
List & Filter Transactions
Transactions are automatically scoped to the authenticated merchant.
Query Parameters
| Param | Type | Example | Description |
|---|---|---|---|
status |
string | completed |
Filter by status: pending, completed, failed, reversed, refunded |
provider |
string | mpesa |
Filter by provider: stripe, mpesa, paypal |
currency |
string | KES |
Filter by 3-letter ISO currency code |
min_amount |
decimal | 100 |
Minimum transaction amount (inclusive) |
max_amount |
decimal | 5000 |
Maximum transaction amount (inclusive) |
since |
datetime | 2025-01-01T00:00:00Z |
Transactions created after this date (ISO 8601) |
until |
datetime | 2025-01-31T23:59:59Z |
Transactions created before this date (ISO 8601) |
search |
string | NEF61H8J60 |
Search by M-Pesa receipt number or idempotency key |
page |
int | 1 |
Page number (default: 1) |
page_size |
int | 50 |
Items per page (default: 50, max: 100) |
Examples
All completed M-Pesa payments this month
High-value failed transactions
Search by M-Pesa receipt number
Stripe payments between $50 and $500
Response
{
"success": true,
"data": [
{
"id": "a1b2c3d4-...",
"merchant_id": "m1m2m3-...",
"amount": "500.00",
"currency": "KES",
"provider": "mpesa",
"status": "completed",
"provider_ref": "NEF61H8J60",
"idempotency_key": "order-456",
"created_at": "2025-06-15T10:30:00Z"
},
{
"id": "e5f6g7h8-...",
"merchant_id": "m1m2m3-...",
"amount": "1200.00",
"currency": "KES",
"provider": "mpesa",
"status": "completed",
"provider_ref": "QKJ82BXRT5",
"idempotency_key": "order-789",
"created_at": "2025-06-14T08:15:00Z"
}
],
"meta": {
"page": 1,
"page_size": 50,
"total": 127
}
}
Empty Result
Idempotency
The Idempotency-Key header is required on POST /v1/payments. If the same key is sent twice:
- Same merchant → returns the original payment (no duplicate charge)
- Different merchant → returns
409 CONFLICT
Error Codes
| Code | HTTP | When |
|---|---|---|
VALIDATION_ERROR |
422 | Missing fields, invalid provider, or credentials not configured |
CONFLICT |
409 | Duplicate idempotency key, or payment in wrong status for operation |
NOT_FOUND |
404 | Payment ID not found or doesn't belong to merchant |