DEVELOPERS
Dashboard →

MyroPay API Reference

The MyroPay API is a RESTful HTTP API. It uses JSON request bodies, returns JSON responses, and uses standard HTTP status codes. All endpoints require HTTPS.

v1.0 · Stable REST · JSON HTTPS required

Base URLs

ENDPOINTS
# Checkout API (SDK-facing — auth via checkout key)
https://checkout.myropay.com/api/

# Business API (auth via JWT or business key)
https://business.myropay.com/api/

# Personal App API (auth via JWT)
https://app.myropay.com/api/
Sandbox mode: Use ck_test_* or sk_test_* keys to test against our sandbox. No real money moves. Behaviour is identical to live.

Authentication

MyroPay supports two authentication methods:

1. Checkout app key (server-side)

Pass your checkout secret key in the X-Checkout-Key header for all Checkout API calls.

HTTP
X-Checkout-Key: ck_sec_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

2. JWT Bearer token (dashboard / business API)

Obtain a JWT via POST /api/auth.php?action=login then pass in the Authorization header:

HTTP
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImFjdG9yX3R5cGUiOiJidXNpbmVzcyJ9.xxx
Security: Never expose secret keys (ck_sec_*, sk_live_*) in client-side code, browser JavaScript, or mobile apps. Use publishable keys (ck_pub_*) on the client side only.

Key types

Key prefixTypeUse caseExpiry
ck_pub_Publishable testClient-side JS onlyNever
ck_sec_Secret testServer-side onlyNever (rotatable)
pk_live_Publishable liveClient-side (KYB required)Never
sk_live_Secret liveServer-side only (KYB required)Never (rotatable)
ck_wh_Webhook secretHMAC-SHA256 signature verificationNever (rotatable)

Errors & responses

All API responses share a consistent JSON envelope:

JSON — success
{
  "success": true,
  "message": "Session created",
  "data": { /* response payload */ }
}
JSON — error
{
  "success": false,
  "message": "amount must be greater than 0",
  "error_code": "VALIDATION_ERROR",
  "status_code": 422
}
HTTP codeMeaningRetry?
200Success
201Created
400Bad request / unknown actionNo
401Authentication failedNo
403Forbidden (KYB/KYC required)No
404Resource not foundNo
409Conflict (duplicate)No
410Gone (session expired)No
422Validation failedNo (fix params)
429Rate limit exceededYes (after retry-after)
500Internal server errorYes (with backoff)
502Upstream gateway errorYes (with backoff)

Pagination

List endpoints return paginated results. Pass page and per_page as query parameters:

Request
GET /api/invoices.php?action=list&page=2&per_page=20
Response
{
  "success": true,
  "data": {
    "data":         [ /* array of items */ ],
    "total":        143,
    "page":         2,
    "per_page":     20,
    "total_pages":  8,
    "has_next":     true,
    "has_prev":     true
  }
}

Rate limits

Endpoint groupDefault limitWindow
Authentication10 requests10 minutes
Checkout session create100 requests1 minute
Transfers / withdrawals20 requests1 minute
General API300 requests1 minute
Analytics endpoints60 requests1 minute

Rate limit headers returned on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. On 429, check the Retry-After header.

Idempotency

For POST requests that create resources or execute transactions, include an idempotency_key in the request body. If the same key is submitted within 24 hours, the original response is returned without re-executing. Prevents duplicate charges from network retries.

JSON body
{
  "amount":          5000,
  "currency_code":   "NGN",
  "idempotency_key": "order_9876_attempt_1"
}

Webhooks

Configure a webhook URL in your checkout app settings. MyroPay sends signed POST requests to your URL when events occur. Always verify the X-MyroPay-Signature header before processing.

import MyroPay from 'myropay-js';

app.post('/webhook', express.raw({type:'application/json'}), (req, res) => {
  const sig    = req.headers['x-myropay-signature'];
  const secret = 'ck_wh_your_secret';
  if (!await MyroPay.verifyWebhook(req.body, sig, secret)) {
    return res.status(401).send('Unauthorized');
  }
  const event = JSON.parse(req.body);
  switch(event.event) {
    case 'checkout.completed': fulfillOrder(event.data); break;
  }
  res.json({ received: true });
});
$payload = file_get_contents('php://input');
$sig     = $_SERVER['HTTP_X_MYROPAY_SIGNATURE'] ?? '';
$secret  = 'ck_wh_your_secret';
$expected= hash_hmac('sha256', $payload, $secret);

if (!hash_equals($expected, $sig)) {
  http_response_code(401); exit;
}
$event = json_decode($payload, true);
if ($event['event'] === 'checkout.completed') {
  fulfillOrder($event['data']);
}
import hmac, hashlib

@app.route('/webhook', methods=['POST'])
def webhook():
    payload   = request.get_data()
    sig       = request.headers.get('X-MyroPay-Signature', '')
    secret    = b'ck_wh_your_secret'
    expected  = hmac.new(secret, payload, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        return 'Unauthorized', 401
    event = request.get_json()
    if event['event'] == 'checkout.completed':
        fulfill_order(event['data'])
    return '', 200

SDKs

JavaScript / TypeScript
Node.js + browser. Includes React hooks.
npm install myropay-js
PHP
PSR-4. Laravel, Symfony, WordPress.
composer require myropay/php-sdk
Python
Sync + async. Django, Flask, FastAPI.
pip install myropay
React
Drop-in <CheckoutButton> component.
npm install @myropay/react

Checkout — Create session

POSThttps://checkout.myropay.com/api/session.php?action=create

Creates a new checkout session. Returns a checkout_url to redirect your customer to. Sessions expire after 30 minutes.

Request body

ParameterTypeRequiredDescription
amountnumberrequiredAmount to charge. In the smallest unit (kobo for NGN, cents for USD, pence for GBP)
currency_codestringrequiredISO 4217 code: NGN, USD, GBP, EUR, GHS, KES
descriptionstringoptionalShown on the checkout page to the customer
customer_emailstringoptionalPre-fills email on the checkout form
customer_namestringoptionalPre-fills name on the checkout form
success_urlstringoptionalRedirect URL after successful payment (overrides app default)
cancel_urlstringoptionalRedirect URL if customer cancels
metadataobjectoptionalKey-value pairs returned in the webhook payload. Max 50 keys, 500 chars per value.
idempotency_keystringoptionalPrevents duplicate sessions on network retries
import MyroPay from 'myropay-js';
const client = new MyroPay('ck_sec_live_xxxx');

const session = await client.checkout.create({
  amount:         500000,   // ₦5,000.00 in kobo
  currency_code:  'NGN',
  description:    'Order #1234',
  customer_email: 'customer@example.com',
  success_url:    'https://mystore.com/success',
  cancel_url:     'https://mystore.com/cancel',
  metadata:       { order_id: 'ORD-9876' },
  idempotency_key: 'order-9876-attempt-1',
});

// Redirect customer to payment page
window.location.href = session.checkout_url;
use MyroPay\Client;
$client = new Client('ck_sec_live_xxxx');

$session = $client->checkout->create([
  'amount'          => 500000,
  'currency_code'   => 'NGN',
  'description'     => 'Order #1234',
  'customer_email'  => 'customer@example.com',
  'success_url'     => 'https://mystore.com/success',
  'cancel_url'      => 'https://mystore.com/cancel',
  'metadata'        => ['order_id' => 'ORD-9876'],
  'idempotency_key' => 'order-9876-attempt-1',
]);

header('Location: ' . $session['checkout_url']);
import myropay
client = myropay.Client("ck_sec_live_xxxx")

session = client.checkout.create(
    amount=500000,
    currency_code="NGN",
    description="Order #1234",
    customer_email="customer@example.com",
    success_url="https://mystore.com/success",
    cancel_url="https://mystore.com/cancel",
    metadata={"order_id": "ORD-9876"},
    idempotency_key="order-9876-attempt-1",
)
return redirect(session["checkout_url"])
curl -X POST https://checkout.myropay.com/api/session.php?action=create \
  -H "X-Checkout-Key: ck_sec_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500000,
    "currency_code": "NGN",
    "description": "Order #1234",
    "customer_email": "customer@example.com",
    "success_url": "https://mystore.com/success",
    "cancel_url": "https://mystore.com/cancel",
    "metadata": { "order_id": "ORD-9876" },
    "idempotency_key": "order-9876-attempt-1"
  }'
201 Created
{
  "success": true,
  "data": {
    "session_id":    42,
    "session_token": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "checkout_url":  "https://checkout.myropay.com/pay/a1b2c3d4e5f6g7h8i9j0",
    "expires_at":    "2025-01-15 13:04:56",
    "amount":        500000,
    "currency":      "NGN"
  }
}
GEThttps://checkout.myropay.com/api/session.php?action=status&token={token}

Retrieves the current status of a checkout session by its token.

ParameterTypeDescription
tokenstringThe session_token returned by create
POSThttps://checkout.myropay.com/api/session.php?action=expire

Immediately expires a pending session (e.g. if the customer cancels in your own UI before redirecting).

JSON body
{ "token": "a1b2c3d4..." }

Invoices

GEThttps://business.myropay.com/api/invoices.php?action=list

Returns paginated list of invoices for the authenticated business.

Query paramTypeDescription
pageintegerPage number (default: 1)
per_pageintegerResults per page (default: 20, max: 50)
statusstringFilter by status: draft | sent | paid | overdue | void
searchstringFull-text search on invoice number, client name, email
POSThttps://business.myropay.com/api/invoices.php?action=create

Creates a new invoice. Returns the invoice ID and a public payment link.

ParameterTypeRequiredDescription
client_namestringrequiredClient display name
client_emailstringrequiredClient email (where invoice email is sent)
itemsarrayrequiredArray of line item objects (see below)
items[].descriptionstringrequiredLine item description
items[].quantitynumberrequiredQuantity
items[].unit_pricenumberrequiredPrice per unit
items[].tax_ratenumberoptionalTax percentage (e.g. 7.5 for 7.5%)
currency_codestringoptionalDefault: business wallet currency
due_datestringoptionalDue date in YYYY-MM-DD format
notesstringoptionalPayment terms / notes shown on invoice
discount_percentnumberoptionalWhole-invoice discount (0–100)
allow_partialbooleanoptionalAllow partial online payments (default: false)
GEThttps://business.myropay.com/api/invoices.php?action=detail&id={id}

Returns full invoice detail including line items and payment history.

POSThttps://business.myropay.com/api/invoices.php?action=send

Sends the invoice to the client's email. Transitions status from draft to sent.

JSON body
{ "id": 42 }
POSThttps://business.myropay.com/api/invoices.php?action=void

Voids an invoice. Cannot be undone. Cannot void a paid invoice.

Wallets

GEThttps://business.myropay.com/api/wallet.php?action=wallets

Returns all active wallets for the authenticated business, ordered by primary first.

200 OK
{
  "wallets": [{
    "id":                 1,
    "currency_code":     "NGN",
    "currency_symbol":   "₦",
    "balance":           "125000.00",
    "locked_balance":    "0.00",
    "is_primary":        1,
    "flw_account_number":"1234567890",
    "flw_bank_name":     "Wema Bank"
  }]
}
GEThttps://business.myropay.com/api/wallet.php?action=lookup&myrotag={tag}

Looks up a user or business by Myrotag or email. Use before initiating a transfer.

GEThttps://business.myropay.com/api/wallet.php?action=virtual-account

Returns the virtual bank account for the primary wallet. Creates one if it doesn't exist yet.

FX Rates

GEThttps://business.myropay.com/api/payments.php?action=fx-rates&base=USD

Returns live exchange rates for a base currency. Rates update every 30 minutes from our liquidity providers.

POSThttps://business.myropay.com/api/payments.php?action=fx-convert

Preview a currency conversion with fee breakdown. Does not execute the conversion.

ParameterTypeDescription
fromstringSource currency code (e.g. USD)
tostringTarget currency code (e.g. NGN)
amountnumberAmount to convert in source currency

Analytics

GEThttps://business.myropay.com/api/analytics.php?action=overview&period=30d

Returns KPI summary for the specified time period.

Query paramValuesDescription
period7d 30d 90d 12m customTime window. Use custom with from and to params.
fromstringStart date YYYY-MM-DD (required when period=custom)
tostringEnd date YYYY-MM-DD (required when period=custom)
GEThttps://business.myropay.com/api/analytics.php?action=revenue-chart&period=30d&granularity=day

Returns time-series revenue data for charting.

Query paramValuesDescription
granularityhour day week monthData point interval

Webhook events

checkout.completed

Payload
{
  "event":     "checkout.completed",
  "app_id":   7,
  "timestamp":1714123456,
  "data": {
    "session": {
      "id":             42,
      "session_token":  "a1b2c3d4...",
      "amount":        500000,
      "currency_code": "NGN",
      "customer_email":"customer@example.com",
      "customer_name": "John Doe",
      "status":        "completed",
      "paid_at":       "2025-01-15 12:34:56",
      "metadata":      { "order_id": "ORD-9876" }
    }
  }
}

transfer.completed

Payload
{
  "event": "transfer.completed",
  "data": {
    "reference":  "MPY-20250115-ABCD1234",
    "amount":     10000,
    "currency":   "NGN",
    "status":     "SUCCESSFUL",
    "bank_name":  "First Bank",
    "account":    "****1234"
  }
}

invoice.paid

Payload
{
  "event": "invoice.paid",
  "data": {
    "invoice_number": "INV-2025-0042",
    "client_email":   "client@company.com",
    "amount":         250000,
    "currency_code":  "NGN",
    "paid_at":        "2025-01-15 14:20:00"
  }
}

payroll.completed

Payload
{
  "event": "payroll.completed",
  "data": {
    "batch_id":        12,
    "batch_name":      "January 2025 Salary",
    "recipient_count": 24,
    "total_amount":    "4800000.00",
    "currency_code":  "NGN",
    "completed_at":   "2025-01-31 14:00:00"
  }
}

Live playground

Test API calls directly. Use your test secret key — no real transactions.

Create checkout session

Secret key
Amount
Currency
Description
Response
Open checkout page →