Overview
The MoneyBee API is organised around resources such as payment intents, payment links, payouts and webhooks. Requests and responses are JSON over HTTPS. Amounts are always integers in minor units with a currency, so AED 100.00 is {"minor": 10000, "currency": "AED"}.
| Environment | Base URL |
|---|---|
| Production | https://api.moneybee.world |
| Sandbox | Shared with your sandbox keys during onboarding |
Need sandbox access? Write to pt@moneybee.world or message us on WhatsApp and we will send keys the same day.
Authentication
Send your secret API key as a bearer token on every request. Keep secret keys on your server; never put them in an app or a web page.
curl https://api.moneybee.world/v1/balances/available \
-H "authorization: Bearer $MONEYBEE_KEY"
Quickstart
1. Install an SDK
npm install @moneybee/sdk
2. Take your first payment
import { MoneyBeeClient } from '@moneybee/sdk';
const mb = new MoneyBeeClient({
baseUrl: 'https://api.moneybee.world',
apiKey: process.env.MONEYBEE_KEY,
});
const payment = await mb.payments.create({
amount: { minor: 10000, currency: 'AED' },
});
console.log(payment.id, payment.status);
3. Listen for the result
Register a webhook endpoint (see Webhooks) and fulfil the order when you receive payment_intent.succeeded.
Payments
A payment intent tracks one payment from creation to settlement. MoneyBee routes it to the best connector under your rules and retries on soft declines.
curl -X POST https://api.moneybee.world/v1/payment_intents \
-H "authorization: Bearer $MONEYBEE_KEY" \
-H "idempotency-key: order-1042" \
-H "content-type: application/json" \
-d '{"amount": {"minor": 10000, "currency": "AED"}, "capture": "manual"}'
With manual capture, capture later, in full or in part. Refunds work the same way.
POST /v1/payment_intents/{id}/capture
POST /v1/payment_intents/{id}/refunds {"amount": {"minor": 2500, "currency": "AED"}}
Payment links
Create a link, share the URL or QR, and get a webhook when it is paid. Links can be single-use or reusable and can expire.
curl -X POST https://api.moneybee.world/v1/payment_links \
-H "authorization: Bearer $MONEYBEE_KEY" \
-H "content-type: application/json" \
-d '{"amount": {"minor": 45000, "currency": "AED"}, "description": "Table for 4, Friday"}'
Payouts
Pay out from your available balance. Payouts can require a second approver (maker-checker) before they settle.
GET /v1/balances/available
POST /v1/payouts
POST /v1/payouts/{id}/approve
GET /v1/reserves
Onboarding
Create an onboarding case for a new merchant, attach documents, and let MoneyBee agents read them and fill the application. You get a webhook as the case moves through review. Merchants who already have an approved live MID skip this step and are provisioned directly.
POST /v1/onboarding/cases
GET /v1/onboarding/cases/{id}
Onboarding webhooks carry x-moneybee-timestamp and x-moneybee-signature headers, verified the same way as below.
Webhooks
Register an HTTPS endpoint with POST /v1/webhooks. Each event is a JSON body {"id", "type", "data"} signed in the mb-signature header:
mb-signature: t=1767225600,v1=5f2b...e9
v1 is the hex HMAC-SHA256 of t + "." + rawBody using your endpoint secret. Reject events older than 300 seconds.
import crypto from 'node:crypto';
export function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (age > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
Respond with any 2xx status quickly and do the work afterwards. Events can arrive more than once, so make your handler safe to repeat using the event id.
Idempotency
Send an idempotency-key header on every POST. If a request times out, retry with the same key and you will get the original result instead of a second payment.
Errors
| Status | Meaning |
|---|---|
400 | The request is invalid. The body says which field. |
401 | Missing or wrong API key. |
402 | The payment was declined. See decline_code. |
404 | The resource does not exist. |
409 | Idempotency key reused with a different body. |
429 | Too many requests. Back off and retry. |
5xx | Something went wrong on our side. Retry with the same idempotency key. |
Testing
In the sandbox, the last two digits of the amount in minor units choose the outcome:
| Amount ends in | Result | Example |
|---|---|---|
01 | Soft decline, insufficient_funds. Cascading retries the next route. | 10001 |
02 | Hard decline, stolen_card. No retry. | 10002 |
03 | requires_action, a 3-D Secure challenge. | 10003 |
| anything else | Succeeds. | 10000 |
Test tokens sbx_hard, sbx_soft_all and sbx_cascade force a hard decline, a soft decline on every route, or a cascade to the second route.
Endpoints
| Area | Endpoints |
|---|---|
| Payments | POST/v1/payment_intents, /capture, /refunds |
| Payment links | POST/v1/payment_links |
| Payouts and balances | /v1/payouts, /approve, /settle, /v1/balances/available, /v1/reserves |
| Disputes | /v1/disputes |
| Crypto | /v1/crypto/deposits |
| Cards | /v1/cards, /fund, /spend |
| Billing | /v1/invoices, /v1/subscriptions |
| Treasury | /v1/fx/quote, /v1/reconcile |
| Platform | /v1/routing, /v1/connectors, /v1/modules, /v1/webhooks, /v1/onboarding/cases |
SDKs and plugins
TypeScript
const mb = new MoneyBeeClient({ baseUrl, apiKey });
const p = await mb.payments.create({ amount: { minor: 10000, currency: 'AED' } });
await mb.payments.capture(p.id);
await mb.payments.refund(p.id, { minor: 2500, currency: 'AED' });
Python
from moneybee import MoneyBeeClient, Amount
mb = MoneyBeeClient("https://api.moneybee.world", key)
payment = mb.create_payment(Amount(10000, "AED"))
Java and PHP
SDKs with the same methods are available for Java and PHP. Ask us for access.
Botim
Botim Pay wallet, cards, Apple Pay, Google Pay, Samsung Pay, holds and subscriptions through one connector. See the Botim via MoneyBee guide.
E-commerce plugins
Ready plugins for WooCommerce, Magento, Medusa and OpenCart. Install, paste your key, and MoneyBee checkout appears at the till.
Stuck? We answer developers directly: pt@moneybee.world or WhatsApp +971 55 861 2001.