SwiftPay — integrator guide
Collect and pay out MTN / Airtel mobile money (RWF) through one API.
Your app talks to SwiftPay — we talk to PalmKash and MoMo.
1. Get an API key (token)
You do not log in to the API with email/password. You use a secret API key.
- Register
- Log in and complete merchant onboarding (business details + a KYC document)
- Wait until an admin approves your merchant
- Open API Keys in the dashboard
- Create a key (
fpk_test_… sandbox or fpk_live_… live)
- Copy it once and store it on your server
Send it on every request:
X-Api-Key: fpk_live_YOUR_KEY
Also accepted: Authorization: Bearer fpk_live_YOUR_KEY
Grant these scopes on the key (the dashboard selects them by default):
payments:read / payments:write — collect and check payment status
transactions:read — list all ledger entries
settlements:read / settlements:write — balance and payouts
webhooks:read / webhooks:write — optional webhook management
Never put a live key in a browser, mobile app, or Git repo. The Try it box on this page is for sandbox keys only.
2. Collect money
POST /payments creates the payment and sends the MoMo prompt. Currency is always RWF.
POST /api/v1/payments
X-Api-Key: fpk_live_...
Content-Type: application/json
{
"amount": "10000",
"method": "MOBILE_MONEY",
"description": "Order #1042",
"metadata": {
"phoneNumber": "2507XXXXXXXX"
}
}
metadata.phoneNumber is required (MTN or Airtel, Rwanda).
If status is still PENDING, send the prompt with:
POST /api/v1/payments/{id}/process
Do not call process again while status is PROCESSING — that would not send a second prompt; we only sync the existing one.
3. Check payment status
Prefer webhooks. To poll:
GET /api/v1/payments/{id}
X-Api-Key: fpk_live_...
Ask PalmKash for a live update:
POST /api/v1/payments/{id}/verify
| Status | Meaning |
|---|
PENDING | Created, prompt not sent yet |
PROCESSING | Waiting on the customer / network |
SUCCESSFUL | Paid — net added to available balance |
FAILED | Declined, timed out, or error |
List everything (collections, payouts, fees):
GET /api/v1/transactions
4. Check balance
GET /api/v1/balance
X-Api-Key: fpk_live_...
{
"success": true,
"data": {
"merchantId": "…",
"availableBalance": "9900.00",
"pendingBalance": "0.00",
"currencyCode": "RWF"
}
}
You can only pay out from availableBalance.
5. Payout — single
Sends money to one phone number from your available balance. Recipient gets the amount; you are charged PalmKash’s withdraw deduction + 100 RWF.
POST /api/v1/payouts
X-Api-Key: fpk_live_...
Content-Type: application/json
{
"phoneNumber": "2507XXXXXXXX",
"amount": 5000,
"recipientName": "Jane Uwase"
}
6. Payout — bulk
Same money movement, many recipients (max 500). CSV from the dashboard also works.
POST /api/v1/bulk-payouts
X-Api-Key: fpk_live_...
Content-Type: application/json
{
"rows": [
{ "phoneNumber": "2507XXXXXXXX", "recipientName": "Jane", "amount": 5000 },
{ "phoneNumber": "25078YYYYYYY", "recipientName": "Eric", "amount": 2500 }
]
}
7. Fees (RWF)
We use PalmKash’s actual deduction, then add SwiftPay’s markup for that merchant.
Platform defaults (change in Settings → Fees; override on a merchant profile):
| Movement | Default markup |
|---|
| Collection | 1% of gross |
| Payout | 100 RWF |
Example — 10,000 RWF collection at the default 1% (PalmKash reports 0 on sandbox until they send a charge):
| Amount |
|---|
| Gross | 10,000 RWF |
| SwiftPay | 100 RWF (1%) |
| Merchant net | 9,900 RWF − PalmKash |
8. Webhooks
In the dashboard, open Webhooks, add your HTTPS URL, and save the signing secret.
Verify X-SwiftPay-Signature = HMAC-SHA256 of the raw body with that secret.
Important events: payment.successful, payment.failed, settlement.completed.
9. Payment links
Share a hosted MoMo checkout without calling collect from your server:
https://pay.swifttech.rw/checkout/{reference}?secret=…
Create links in the dashboard (Payments) or POST /api/v1/payments/links.
Go-live checklist
Security
- Keep API keys on the server only
- Always verify webhook signatures
- You do not need PalmKash credentials — SwiftPay owns that connection