Vouchers API
Simple, secure, and instant voucher system.
Live Base URLhttps://payments.pesavoucher.com/api/v1
Sandbox Base URL (for testing)https://sandbox.payments.pesavoucher.com/api/v1
| Direction | Voucher Created By | Redeemed On | API You Call |
|---|---|---|---|
| Customer → You (Deposit) | Customer (on pesavoucher.com) | Your website/app | /validate_voucher + /redeem_voucher |
| You → Customer (Withdrawal) | You (merchant) | pesavoucher.com | Only /create_voucher |
Key Points:
- Customers can paste vouchers on your site (for deposits)
- You can send vouchers to customers (for payouts)
All API requests require authentication using API credentials in request headers:
X-API-KEY: your_api_key_hereX-API-SECRET: your_api_secret_here
Generate a voucher that your users can redeem on the PesaVoucher platform (e.g. withdrawal, refund, bonus).
POST /create_voucher
Content-Type: application/jsonX-API-KEY: your_api_keyX-API-SECRET: your_api_secret
{"email": "customer@example.com","amount": 750.00,"unique_id": "7373737","currency": "KES","callback_url": "https://yoursite.com/webhook/voucher-status","message": "Your withdrawal request has been approved!"}
Field Descriptions:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Customer's email address (must be registered on PesaVoucher) |
amount | float | Yes | Voucher amount (min: 50.00, max: 150,000.00 KES) |
currency | string | Yes | Currency code (currently only "KES" supported) |
unique_id | string | No | Your internal reference/transaction ID |
callback_url | string | No | URL to receive async status updates |
message | string | No | Personal message to include in the voucher email |
{"success": true,"voucher_code": "NLV-53SKM7C0HNDSAZAM","currency": "KES","amount": 750.00,"transaction_id": "463G4HDIF3","unique_id": "7373737"}
{"success": false,"error": "User not found"}
Common Error Messages:
"Missing required fields: email, amount, currency""Minimum voucher amount is KES 50""Maximum voucher amount is KES 150000""Currency must be KES""User not found""Insufficient merchant balance"
What Happens Next:
- The voucher is instantly created and credited to the user's wallet
- A branded email with the voucher PDF is sent to the customer
- The voucher can be redeemed on pesavoucher.com
- If
callback_urlwas provided, you'll receive status updates
Check if a voucher is valid before redeeming it. This is a read-only operation that doesn't modify any data.
POST /validate_voucher
Content-Type: application/jsonX-API-KEY: your_api_keyX-API-SECRET: your_api_secret
{"voucher_code": "NLV-DTUMJQ6ILG9WNISE"}
{"success": true,"valid": true,"voucher": {"code": "NLV-DTUMJQ6ILG9WNISE","amount": 750.00,"currency": "KES","status": "active","is_active": true,"recipient": {"name": "Jane Doe","email": "customer@example.com"}}}
Already Redeemed:
{"success": true,"valid": false,"validation_errors": ["Voucher already redeemed on 2025-11-20 10:30:00"]}
Not Found or Deleted:
{"success": true,"valid": false,"validation_errors": ["Voucher not found or has been deleted"]}
Platform-Only Voucher:
{"success": true,"valid": false,"validation_errors": ["Voucher is only redeemable at PesaVoucher platform"]}
Validation Error Types:
"Voucher not found or has been deleted""Voucher is not active""Voucher already redeemed on [date]""Voucher is only redeemable at PesaVoucher platform, not at merchant locations""Voucher does not belong to your merchant account"
This is the core action that marks the voucher as used and credits your merchant account.
POST /redeem_voucher
Content-Type: application/jsonX-API-KEY: your_api_keyX-API-SECRET: your_api_secret
{"voucher_code": "NLV-DTUMJQ6ILG9WNISE","currency": "KES","callback_url": "https://yoursite.com/webhook/redemption","note": "November cashout","redeemed_by": "user_8871"}
Field Descriptions:
| Field | Type | Required | Description |
|---|---|---|---|
voucher_code | string | Yes | The voucher code to redeem |
currency | string | Yes | Currency code (must be "KES") |
callback_url | string | No | URL to receive async redemption status |
note | string | No | Internal note for your records |
redeemed_by | string | No | Staff/cashier name or your internal user ID |
{"success": true,"message": "Voucher redeemed successfully","redemption": {"voucher_code": "NLV-DTUMJQ6ILG9WNISE","amount": 750.00,"currency": "KES","redeemed_at": "2025-11-21 14:22:10","redeemed_by": "user_8871","customer": {"name": "Jane Doe","email": "customer@example.com"}}}
{"success": false,"error": "Voucher has already been redeemed on 2025-11-20 10:30:00"}
Common Error Messages:
"Missing required field: voucher_code or currency""Currency must be KES""Voucher not found or does not belong to your merchant account""Voucher has been deleted""Voucher is not active""Voucher is only redeemable at PesaVoucher platform, not at merchant locations""Voucher has already been redeemed on [date]""Insufficient wallet balance"
What Happens:
- The voucher is marked as redeemed
- The amount is deducted from the customer's wallet
- Your merchant account is credited
- The customer receives an email notification
- If
callback_urlwas provided, you'll receive a confirmation callback
Both /create_voucher and /redeem_voucher endpoints support optional callback URLs for asynchronous notifications.
When you provide a callback_url in your request, PesaVoucher will send an HTTP POST request to that URL with the operation status.
Headers:
Content-Type: application/jsonX-Callback-Type: STK-Push
Success Payload:
{"success": true,"message": "Voucher created successfully","voucher_code": "NLV-53SKM7C0HNDSAZAM","amount": 750.00,"transaction_id": "463G4HDIF3","unique_id": "7373737","currency": "KES","timestamp": "2025-11-26 14:30:45"}
Failure Payload:
{"success": false,"message": "Failed to create voucher","voucher_code": null,"amount": 750.00,"transaction_id": "463G4HDIF3","unique_id": "7373737","currency": "KES","timestamp": "2025-11-26 14:30:45"}
Success Payload:
{"voucher_code": "NLV-DTUMJQ6ILG9WNISE","amount": 750.00,"redeemed_at": "2025-11-26 14:35:20","redeemed_by": "user_8871","customer": {"name": "Jane Doe","email": "customer@example.com"},"currency": "KES","timestamp": "2025-11-26 14:35:20"}
Failure Payload:
{"success": false,"message": "Failed to redeem voucher","voucher_code": "NLV-DTUMJQ6ILG9WNISE","unique_id": null,"currency": "KES","timestamp": "2025-11-26 14:35:20"}
- Always respond with HTTP 200 OK - Even if you encounter an error processing the callback
- Verify the callback - Check that the
voucher_codeorunique_idmatches your records - Be idempotent - Handle duplicate callbacks gracefully (same voucher_code may be sent multiple times)
- Timeout handling - PesaVoucher waits 10 seconds for your response
- Security - Validate the callback is coming from PesaVoucher's IP addresses
- Use HTTPS - Always use HTTPS URLs for your callback endpoints
<?php// callback-handler.php$payload = json_decode(file_get_contents('php://input'), true);if (!$payload) {http_response_code(400);exit('Invalid payload');}// Log the callbackerror_log("Received callback: " . json_encode($payload));// Verify and process based on typeif (isset($payload['voucher_code'])) {$voucherCode = $payload['voucher_code'];$success = $payload['success'] ?? false;if ($success) {// Update your database// Send notifications// Update user balance} else {// Handle failure// Alert admin}}// Always respond with 200 OKhttp_response_code(200);echo json_encode(['received' => true]);
Implement a user-friendly interface for voucher redemption:
┌──────────────────────────────────────┐│ Redeem Your Voucher │├──────────────────────────────────────┤│ Paste your voucher code below: ││ ││ [ NLV-___________________ ] ││ ││ [ Redeem → ] │└──────────────────────────────────────┘
Recommended Flow:
- Single input field - Keep it simple
- Real-time validation - Call
/validate_voucheron blur/paste - Amount preview - Show voucher amount when valid
- Conditional button - Only enable "Redeem" when
valid: true - Success screen - Clear confirmation: "✓ KES 750 added to your account"
- Error handling - Show friendly error messages from validation
Use the sandbox environment for testing without affecting real data:
Sandbox URL:https://sandbox.payments.pesavoucher.com/api/v1
Testing Features:
- All vouchers created start with
TEST-NLV-... - No actual emails sent (check logs instead)
- Redemption works instantly
- All endpoints return realistic test data
- Separate test credentials provided
Test Scenarios to Cover:
- Create voucher → validate → redeem (happy path)
- Attempt to redeem twice (should fail)
- Validate non-existent voucher
- Create voucher with insufficient balance
- Test callback URL handling
- Always validate first - Call
/validate_voucherbefore showing redemption UI - Check ownership - Ensure voucher belongs to your merchant account
- Enable redemption conditionally - Only when
"valid": true - Secure storage - Never store voucher codes in localStorage plain text
- Idempotency - Handle duplicate redemption attempts gracefully
- HTTPS only - Always use secure connections
- Rate limiting - Implement rate limits on your redemption endpoint
- Audit logs - Keep detailed logs of all voucher operations
- Callback verification - Validate callbacks are from PesaVoucher
- Error handling - Show user-friendly messages, log technical details
All endpoints follow a consistent error response format:
{"success": false,"error": "Human-readable error message"}
HTTP Status Codes:
200- Success or validation response400- Bad request (invalid input)401- Authentication failed403- Forbidden (voucher doesn't belong to merchant)404- Resource not found500- Internal server error
Recommended Client-Side Handling:
try {const response = await fetch('/api/v1/redeem_voucher', {method: 'POST',headers: {'Content-Type': 'application/json','X-API-KEY': apiKey,'X-API-SECRET': apiSecret},body: JSON.stringify(payload)});const data = await response.json();if (data.success) {// Handle successshowSuccess(`KES ${data.redemption.amount} credited!`);} else {// Handle errorshowError(data.error);}} catch (error) {// Network or parsing errorshowError('Unable to process request. Please try again.');}
For API support or to request merchant credentials:
- Email: support@pesavoucher.com
- Documentation: https://docs.pesavoucher.com
- Status Page: https://status.pesavoucher.com
Version 1.0 (Current)
- Initial API release
- Support for KES currency
- Create, validate, and redeem voucher endpoints
- Callback/webhook support
- Sandbox environment