MyRoPay API
A complete payments infrastructure: send money, collect payments, hold funds in escrow, pay bills, link bank accounts, and build full checkout experiences — all through a single unified REST API.
Authentication
MyRoPay uses short-lived JWT access tokens (15 min) paired with long-lived refresh tokens (30 days). Every authenticated request sends the access token as a Bearer header.
# Every authenticated request
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Your app should proactively refresh 2 minutes before expiry, or immediately on any 401 response. The SDK (app.js) handles this automatically via setupTokenRefresh().
Errors & Rate Limiting
| HTTP Code | Meaning | Common cause |
|---|---|---|
| 200 | Success | |
| 400 | Bad Request | Missing or malformed fields |
| 401 | Unauthorized | Missing, expired, or invalid token |
| 403 | Forbidden | KYC required, wrong account type, PIN wrong |
| 404 | Not Found | Resource not found or doesn't belong to you |
| 409 | Conflict | Duplicate idempotency key within 24h |
| 429 | Rate Limited | Too many requests — see retry_after |
| 503 | Unavailable | Database momentarily unavailable |
// All errors follow this shape
{ "success": false, "message": "Insufficient balance.", "retry_after": 42 }
Idempotency. Financial endpoints accept an optional X-Idempotency-Key header. Send a UUID — repeat calls within 24 h return the original response without re-executing the transaction.
X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Rate limits. Per-IP defaults: login 10/5 min · register 5/5 min · OTP resend 3/5 min · general 300/min. Headers on limited responses: X-RateLimit-Remaining, X-RateLimit-Reset.
Checkout SDK
One script tag. Four integration modes. No gateway keys in your frontend — all keys remain on the server. Works on any HTML page or framework.
Install
Add to any HTML page. The global MyRoPay object is available immediately after the script loads.
<script src="https://app.myropay.com/myropay.js"></script>
MyRoPay.checkout({
publicKey: 'pk_live_...',
amount: 5000, // major unit — ₦5000, not 500000 kobo
currency: 'NGN',
orderRef: 'ORD-001',
description: 'Premium plan',
customer: { email: 'buyer@email.com', name: 'John Doe' },
escrow: true,
escrowCondition: 'Software delivered and tested',
onSuccess: (data) => console.log('Paid:', data),
onClose: () => console.log('Closed'),
onError: (msg) => console.error(msg),
});
// Node.js / PHP backend — use your SECRET key
const session = await MyRoPay.createSession({
publicKey: 'sk_live_...',
amount: 15000, currency: 'NGN',
orderRef: 'ORD-002',
successUrl: 'https://yoursite.com/thanks',
cancelUrl: 'https://yoursite.com/cart',
});
res.redirect(session.checkout_url);
const { destroy } = await MyRoPay.inline({
container: '#checkout-box',
publicKey: 'pk_live_...',
amount: 2500, currency: 'NGN',
});
MyRoPay.createButton({
container: '#pay-here',
label: 'Pay with MyRoPay',
publicKey: 'pk_live_...',
amount: 5000, currency: 'NGN',
});
const result = await MyRoPay.verify(sessionId, 'sk_live_...');
if (result.status === 'completed' && result.amount === expectedAmount) {
// fulfil order
}
| Parameter | Type | Notes |
|---|---|---|
| publicKey | req | pk_live_ or pk_test_ merchant key |
| amount | req | Major unit (₦5000, $50.00) |
| currency | req | ISO 4217: NGN, USD, GHS, KES… |
| orderRef | opt | Your internal order ID stored on the session |
| customer.email | opt | Pre-fills the buyer email on the checkout page |
| escrow | opt | Boolean — requires merchant escrow feature enabled |
| escrowCondition | opt | Plain-text delivery condition shown to buyer |
| escrowDeadlineDays | opt | Days before auto-dispute; 1–90, default 7 |
| paymentMethods | opt | Array — card, bank_transfer, ussd, mobile_money, wallet |
| successUrl / cancelUrl | opt | Redirect URLs for non-modal flows |
| brandColor / brandLogo | opt | Customize the checkout page appearance |
| expiresInMinutes | opt | Session TTL (5–10080 min, default 1440) |
Auth Endpoints
Create an account. Returns access + refresh token immediately — no separate login step needed.
{
"first_name": "Dami",
"last_name": "Ehimare",
"email": "dami@example.com",
"phone": "+2348100000001",
"password": "StrongPass1!",
"account_type": "personal", // "personal" | "business"
"country_code": "NG", // ISO 3166-1 alpha-2
"referral_code":"DAMI1234" // optional
}{
"success": true,
"token": "eyJhbGci...", // 15-min access token
"refresh_token": "a3f2b9...", // 30-day refresh token
"user": {
"id": 42, "uuid": "4c758d...",
"first_name": "Dami", "last_name": "Ehimare",
"myrotag": "damiehimare",
"email": "dami@example.com",
"currency": "NGN", "currency_symbol": "₦",
"country_code": "NG", "country": "Nigeria",
"kyc_status": "pending", "kyc_level": 0,
"pin_set": false, "email_verified": false,
"account_type": "personal",
"referral_code": "DAMI7A2C"
}
}Authenticate with email + password. Include totp_code if 2FA is enabled on the account.
{ "email": "dami@example.com", "password": "StrongPass1!", "totp_code": "123456" }
Exchange a refresh token for a new access + refresh pair. Tokens rotate on every use — store the new refresh token immediately.
{ "refresh_token": "b7d3e1..." }
Returns the authenticated user's full profile: balance, KYC status, pin_set, referral code, and unread notification count.
Change your @myrotag. Maximum 3 changes per year. Body: { "myrotag": "newhandle" }
Verify email address using the 6-digit OTP sent at registration. Body: { "otp": "123456" }
Trigger a password reset email. Always returns 200 to prevent user enumeration. Body: { "email": "..." }
Complete the reset. Body: { "token": "...", "password": "NewPass1!" }
Generate a TOTP secret and QR code URI. Returns secret and qr_url. The user scans it in an authenticator app then confirms via confirm-2fa.
Activate 2FA by verifying the first TOTP code. Returns 10 single-use recovery codes — store immediately. Body: { "code": "123456" }
Disable 2FA by confirming with a current TOTP code. Body: { "code": "123456" }
Wallet & Transfers
Query balances, send money to any @myrotag or email, request funds, and access full transaction history with search and filters.
Primary wallet balance plus locked funds (in active escrows) and monthly flow totals.
{
"success": true,
"balance": "42910.24",
"locked": "1200.00", // held in active escrows
"currency": "NGN", "symbol": "₦",
"monthly_in": "55000.00",
"monthly_out": "12090.00"
}
Send money to any user. The recipient field accepts @myrotag, email, or phone. Requires PIN or active biometric session. KYC Level 0 limit: $200 per transfer.
{
"recipient": "@alexvance", // @tag | email | phone
"amount": 1500,
"pin": "1234", // or "biometric_session": "..."
"note": "Thanks for dinner"
}{
"success": true,
"message": "Transfer of ₦1,500 to @alexvance successful!",
"transaction_id": "4c758d30-60a6-...",
"new_balance": "41395.86",
"receipt": {
"amount": "1500.00", "fee": "0.10",
"from": { "name": "Dami E.", "tag": "@damiehimare" },
"to": { "name": "Alex V.", "tag": "@alexvance" },
"status": "completed", "timestamp": "2025-04-23T14:20:00Z"
}
}Find a user by @myrotag, email, or phone. Returns id, first_name, last_name, myrotag, avatar, currency.
Request payment from another user. They receive an in-app notification with a one-tap pay button. Body: { "target_email": "...", "amount": 500, "note": "..." }
Pay a received payment request. Body: { "request_id": 14, "pin": "1234" }
Paginated transaction history with full-text search and type filters.
| Query param | Description | |
|---|---|---|
| page | opt | Page number (default 1) |
| limit | opt | Per page: 5–50 (default 20) |
| type | opt | deposit · withdrawal · transfer_in · transfer_out · bill_payment · escrow_lock · escrow_release |
| q | opt | Full-text search across reference, description, sender/receiver name |
| from / to | opt | Date range filter — YYYY-MM-DD |
Deposits
Add money to a wallet. The server selects the best gateway automatically based on the user's country — no public keys are ever sent to the browser.
Smart gateway init. Returns different payloads depending on the selected gateway. Optionally pass method to force a specific gateway.
{
"amount": 5000,
"currency": "NGN",
"method": "auto" // "auto"|"paystack"|"flutterwave"|"checkout"
}// Nigeria → Paystack (access_code only, no public key exposed)
{ "gateway":"paystack", "access_code":"0peioxfhpn", "reference":"MRP_PS_3F2A1B8C" }
// Africa → Flutterwave (hosted payment link, no key)
{ "gateway":"flutterwave", "payment_link":"https://checkout.flutterwave.com/...", "tx_ref":"MRP_FLW_7C4E2A" }
// Global → MyRoPay Checkout session
{ "gateway":"checkout", "checkout_url":"https://app.myropay.com/pay?session=cs_live_...", "session_id":"cs_live_4f2b..." }Call after PaystackPop onSuccess fires. Body: { "reference": "MRP_PS_..." }. Returns new_balance.
Call after Flutterwave redirect. Body: { "tx_ref": "...", "transaction_id": "..." }.
Server-to-server event from Paystack. Validated using X-Paystack-Signature HMAC-SHA512. Register at: myropay.com/app/api/deposit.php?action=paystack-webhook
Server-to-server event from Flutterwave. Validated using VERIF-HASH header. Register at: myropay.com/app/api/deposit.php?action=flutterwave-webhook
Payouts
Withdraw to a local bank account, mobile money wallet, or PayPal. All withdrawals require verified KYC. Funds are debited immediately and processed within 24 hours.
Supported banks for a given country code. NG uses Paystack's list; other countries use Flutterwave.
Verify an account number before adding it. Params: account_number, bank_code. Returns the account holder name (Nigeria only, powered by Paystack).
Save a bank account for future withdrawals. Account numbers are AES-256-CBC encrypted at rest. Body: { "bank_name", "bank_code", "account_number", "account_name" }
Start a withdrawal. KYC must be verified. Fee: max($1.00, 1%). Funds are debited from the wallet immediately.
{
"method": "bank", // "bank" | "paypal" | "mobile_money"
"amount": 10000,
"pin": "1234",
"details": { "bank_id": 3 } // from /payout.php?action=my-banks
}{
"success": true,
"message": "Withdrawal initiated. Processing within 24h.",
"transaction_id": "7f3b9d...",
"new_balance": "32810.00"
}Transactions
Paginated list. Query params: page, limit, type, q (search), from, to.
Single transaction with full sender and receiver names, @tags, avatars, and fee breakdown.
Export up to 5,000 transactions as JSON. Date range params: from, to. Returns a file attachment.
Escrow
Lock funds until delivery conditions are met. Either party can raise a dispute — MyRoPay reviews and resolves within 48 hours. Escrow fee: 1.5% of the locked amount.
Create and fund an escrow in one step. Amount + 1.5% fee debited immediately and locked.
{
"beneficiary": "@alexvance", // @tag or email
"amount": 50000,
"title": "Website redesign",
"description": "5 pages, 2 revisions, delivered in 2 weeks",
"due_date": "2025-05-15",
"pin": "1234"
}
Release locked funds to the beneficiary. Only the initiator can call this. Body: { "uuid": "...", "pin": "1234" }
Raise a dispute. Either party can call this. Funds are frozen until resolved by MyRoPay admin. Body: { "uuid": "...", "reason": "...", "evidence": "https://..." }
Submit your response to an open dispute. Body: { "dispute_uuid": "...", "response": "...", "evidence": "..." }
All escrows where you are initiator or beneficiary. Returns status, counterparty details, amounts, and any open dispute IDs.
Payment Links
Shareable links that let anyone pay you without a MyRoPay account. Personal links use your permanent @myrotag URL; business links generate a UUID URL with configurable usage limits and expiry.
Business: https://myropay.com/pay/{uuid} — configurable limits and expiry.
{
"title": "Design Bundle v2",
"description": "Includes logo, brand guide, and social kit",
"amount": 29999, // 0 = open amount (payer sets it)
"is_fixed": true,
"usage_limit": 50, // 0 = unlimited
"expires_days": 30 // 0 = never expires
}
Returns link (full URL), uuid, and times_used.
All your payment links with times_used, active flag, expiry date, and full payer URLs.
Enable or disable a link. Body: { "id": 7 }. Returns the new active boolean.
Permanently delete a payment link. Body: { "id": 7 }
Bill Payments NG only
Pay airtime, data, electricity, cable TV, and internet bills for Nigerian users via Flutterwave's bill payment infrastructure.
Returns supported categories: AIRTIME, DATA_BUNDLE, POWERBI (electricity), DSTV, GOTV, STARTIMES, WATERBI, NETPLUSINC. Each category includes its valid billers.
Validate a customer number before payment (e.g. electricity meter, SmartCard number). Body: { "item_code": "...", "biller_code": "...", "customer": "12345678" }
{
"type": "AIRTIME",
"customer": "08012345678",
"amount": 1000,
"pin": "1234"
}
Savings Goals
Create named savings goals with target amounts, target dates, and optional automatic recurring contributions. Cron runs auto-saves daily.
{
"name": "New MacBook",
"target_amount": 450000,
"target_date": "2025-12-01",
"auto_save": true,
"auto_save_amount": 15000,
"auto_save_frequency": "monthly" // "daily"|"weekly"|"monthly"
}
Manually top up a goal from your wallet. Body: { "id": 3, "amount": 5000, "pin": "1234" }. Returns new_amount and progress percentage.
Withdraw funds from a goal back to your wallet. Body: { "id": 3, "amount": 5000, "pin": "1234" }
Virtual Cards
Virtual Visa cards for online payments. KYC Level 1 required to create. Card numbers are AES-256-CBC encrypted at rest — only decrypted on explicit reveal with PIN.
Create a new virtual card. Returns masked details only. Full number available via reveal. Body: { "pin": "1234" }
Decrypt and return full card number, CVV, and expiry. Requires PIN. Body: { "id": 1, "pin": "1234" }
Toggle card freeze/unfreeze. Body: { "id": 1 }. Returns new status.
KYC Verification
Three tiers of identity verification with increasing limits. Documents are reviewed by MyRoPay's compliance team within 24 hours.
| Level | Label | Daily limit | Requirement |
|---|---|---|---|
| 0 | Starter | $200 equiv. | Email verified |
| 1 | Standard | $5,000 | Government ID + selfie |
| 2 | Verified | $50,000 | Address proof + BVN (NG) |
Returns current tier, document submission status per tier, and per-tier daily limits.
Submit a KYC document. Multipart form-data (not JSON). Max 5 MB per file. Accepted: JPEG, PNG, PDF.
POST /kyc.php?action=upload
Content-Type: multipart/form-data
doc_type: "govt_id" # govt_id|selfie|proof_of_address|passport|drivers_license
tier: 1
document: <binary>
Nigerian BVN verification. BVN is hashed (SHA-256) immediately on receipt — never stored in plain text. Body: { "bvn": "12345678901" }
Invoices Business accounts
Create, send, and track professional invoices with line items, taxes, and payment links. Paid invoices credit your wallet automatically.
{
"client_name": "Acme Corp",
"client_email": "billing@acme.com",
"currency": "NGN",
"due_date": "2025-05-30",
"tax_rate": 7.5,
"items": [
{ "description": "Logo design", "quantity": 1, "unit_price": 80000 },
{ "description": "Brand guide", "quantity": 1, "unit_price": 45000 }
]
}
Returns uuid, invoice_number (e.g. INV-A3F2B901), subtotal, tax total, and grand total.
Email the invoice PDF to the client. Changes status from draft → sent. Body: { "uuid": "..." }
All invoices with status. Query param status: draft · sent · paid · overdue.
Payroll Business accounts
Send salaries and payments to multiple MyRoPay users in a single request. Recipients receive funds instantly.
{
"name": "April 2025 Payroll",
"recipients": [
{ "myrotag": "@alice", "amount": 250000, "note": "April salary" },
{ "myrotag": "@bob", "amount": 180000, "note": "April salary" }
]
}
Wallet must cover the total. Returns batch_id, total_amount, and per-recipient status.
Payroll batches with totals, recipient count, and completion status.
Webhook Management Business accounts
{
"name": "Production endpoint",
"url": "https://yoursite.com/hooks/myropay",
"events": ["payment.success", "escrow.released", "payout.completed"]
}
Returns a secret for HMAC verification — shown once, store it immediately.
Send a test ping to your endpoint. Body: { "id": 5 }. Returns the HTTP status code your server responded with.
Analytics Business accounts
KPI summary for last N days (7 · 30 · 90). Returns revenue, spending, net, transaction counts, payment link stats, invoice totals, and payroll disbursements.
Daily inflow/outflow series. Returns array of { "date": "2025-04-01", "inflow": 55000, "outflow": 12000 }
Top 10 users by total amount sent to your account in the last 90 days.
Breakdown of transaction volume by type (deposit, transfer, escrow, bill, withdrawal) for charting.
Checkout API
Create and manage checkout sessions directly from your backend using your merchant secret key. Supports standard, instalment, donation, and subscription payment types.
POST https://app.myropay.com/checkout/api/sessions.php
Authorization: Bearer sk_live_...
{
"amount": 15000,
"currency": "NGN",
"order_ref": "ORD-042",
"description": "Premium plan — 1 month",
"session_type": "standard", // standard|instalment|donation|subscription
"payment_methods": ["card", "bank_transfer", "ussd"],
"customer": { "email": "buyer@example.com", "name": "Alex Vance" },
"success_url": "https://yoursite.com/thanks?order=ORD-042",
"cancel_url": "https://yoursite.com/cart",
"escrow": false,
"brand_color": "#5B8DF5",
"expires_in": 1440 // minutes — default 1440 (24h)
}{
"session_id": "cs_live_4f2b9a3c8e1d...",
"checkout_url": "https://app.myropay.com/pay?session=cs_live_4f2b...",
"amount": 15000,
"currency": "NGN",
"status": "pending",
"expires_at": "2025-04-24T14:00:00Z"
}Verify a session after payment. Always verify server-side before fulfilling an order. Check status === "completed" and confirm amount matches your expected value.
Create a merchant app and get pk_live_ / sk_live_ keys. Limited to 5 apps per account.
{
"app_name": "My Store",
"app_url": "https://mystore.com",
"support_email": "support@mystore.com",
"mode": "live" // "live"|"test"
}
Bank Account Linking NG only
Link Nigerian bank accounts via Mono to access real-time balance data and bank statements for financial analysis and income verification.
Returns public_key and user_ref. Pass these to the Mono Connect widget on your frontend.
Exchange the Mono auth code returned by the Connect widget. Stores the linked account and returns account details. Body: { "code": "mono_auth_code_..." }
Bank statement for a linked account. Returns last 90 days of transactions.
Remove a linked bank account. Body: { "account_id": "..." }
Notifications
Paginated notifications with unread count. Query param: page.
Returns { "count": 4 } — fast endpoint for polling badge counts.
Mark all notifications as read. No body required.
Account Settings
Update display name and phone. Body: { "first_name", "last_name", "phone" }
Body: { "current_password", "new_password" }. Triggers a security alert email and invalidates all other sessions.
Set or change the 4-digit transaction PIN. Body: { "pin": "1234", "confirm_pin": "1234" }
Toggle a preference flag. Body: { "key": "push_notifications", "value": 1 }. Valid keys: push_notifications · email_notifications · login_alerts · weekly_digest_enabled · roundup_enabled.
List all active login sessions with device, browser, IP, and last active time.
Log out all other sessions except the current one. No body required. Good for "logout everywhere" on suspicious activity.
Personal API Keys
Generate an mrp_-prefixed personal key for server-side automations and integrations. Use it anywhere you'd use a JWT Bearer token — it never expires unless explicitly revoked.
Check if a key exists. Returns has_key, key_masked (e.g. mrp_4a7b****c6b7), and key_prefix. Full key is never re-exposed.
Generate or regenerate a key. Requires PIN. The full key is returned once only — copy it immediately.
{ "success": true, "api_key": "mrp_4a7b2c9e1f3d0e8a5c6b..." }
Immediately invalidate the current key. All requests using it will return 401. Body: { "pin": "1234" }
Support Tickets
{
"subject": "Deposit not reflecting",
"message": "I paid ₦5000 via Paystack at 2:14pm but...",
"category": "payment", // payment|account|kyc|technical|other
"priority": "high" // low|normal|high|urgent
}
All your tickets with status (open, in_progress, resolved, closed) and unread reply count.
Add a reply to an open ticket. Body: { "ticket_id": 12, "message": "..." }
Webhook Events
MyRoPay delivers HMAC-signed POST requests to your registered endpoints in real time. All events share a common envelope and are retried with exponential backoff on failure.
// Node.js — always verify before processing
const crypto = require('crypto');
const rawBody = req.rawBody; // raw Buffer, not parsed JSON
const sig = req.headers['x-myropay-signature'];
const expected = 'sha256=' + crypto.createHmac('sha256', WEBHOOK_SECRET)
.update(rawBody).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).end();
}
res.status(200).end(); // ack fast, process async
const event = JSON.parse(rawBody);
{
"event": "payment.success",
"timestamp": "2025-04-23T14:22:00Z",
"api_version": "3.1",
"data": { /* event-specific payload */ }
}
| Event | Trigger |
|---|---|
| payment.success | Checkout session paid and completed |
| payment.failed | Payment attempt abandoned or declined |
| deposit.confirmed | Wallet deposit credited (any gateway) |
| transfer.received | P2P transfer credited to your wallet |
| payout.completed | Withdrawal processed and sent |
| payout.failed | Withdrawal failed — funds returned to wallet |
| escrow.funded | Escrow created and funds locked |
| escrow.released | Initiator releases to beneficiary |
| escrow.disputed | Either party raises a dispute |
| escrow.resolved | Admin resolves the dispute |
| kyc.approved | KYC document reviewed and approved |
| kyc.rejected | KYC document rejected — resubmission needed |
| invoice.paid | Invoice paid via checkout link |
| invoice.overdue | Invoice past due date with no payment |
| payroll.completed | Payroll batch fully processed |