API Documentation
MonCashConnect is a REST API for accepting MonCash payments in your application, WordPress site, or SaaS — without managing OAuth, tokens, or the complexity of the official gateway.
Every request is authenticated with your project's secret key sk_proj_…. HMAC-SHA256-signed webhooks guarantee the integrity of payment notifications.
https://api.moncashconnect.com/v1All requests must use HTTPS. The body of
POST requests must be application/json.npx -y @moncashconnect/mcp connects Cursor, Claude Code, or Codex to MonCashConnect read-only, with an sk_ro_… key that can't move money. See the MCP guide.Quickstart
Integrate MonCash in under 5 minutes. Here is the minimal end-to-end flow.
Create a project in the dashboard
Go to Developer → Projects and create a project. You will receive a secret key sk_proj_… — keep it somewhere safe, it is shown only once.
Create a payment server-side
curl -X POST https://api.moncashconnect.com/v1/pay-create \
-H "Authorization: Bearer sk_proj_xxxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 500,
"referenceId": "order_001",
"returnUrl": "https://votresite.com/merci"
}'The response contains paymentUrl — redirect your customer to this URL.
Redirect the customer to MonCash
const { paymentUrl } = await res.json();
// Server-side (Express, Next.js, etc.)
res.redirect(paymentUrl);Receive confirmation via webhook
Configure a webhook URL in your project. MonCashConnect sends an HMAC-SHA256-signed POST as soon as the payment is confirmed by MonCash.
import { constructEvent, MonCashError } from "@moncashconnect/sdk";
// Express — read the raw body BEFORE JSON.parse
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
try {
const event = constructEvent(
req.body,
req.headers["x-mcc-signature"],
req.headers["x-mcc-timestamp"],
process.env.MCC_WEBHOOK_SECRET,
);
if (event.event === "payment.completed") {
// Credit the order in your database
}
res.sendStatus(200);
} catch (err) {
if (err instanceof MonCashError) return res.status(err.statusCode).send(err.message);
res.sendStatus(500);
}
});Authentication
Each project has a unique secret key prefixed with sk_proj_. Pass it in the Authorization header of every request.
curl -H "Authorization: Bearer sk_proj_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
https://api.moncashconnect.com/v1/pay-balanceAllowed domains (CORS)
If you configure allowed domains on your project, requests from other origins will be rejected with a 403. Leave the list empty to allow all origins (server-side use only).
localhost, your staging URLs, and previews. Add each environment (prod, staging, dev) explicitly.Create a payment
https://api.moncashconnect.com/v1/pay-createCreates a pending transaction and returns a MonCash payment URL. Redirect your customer to this URL. The transaction expires after 15 minutes.
Parameters (JSON body)
| Nom / Code | Type / Limite | Description |
|---|---|---|
| amount* | integer | Amount in HTG, integer between 1 and 1,000,000 |
| referenceId* | string | Unique identifier [a-zA-Z0-9-_], 100 characters max. Unique per project. |
| returnUrl | string | HTTPS return URL after payment — strongly recommended |
| customerName | string | Customer name — displayed on the MonCash page (optional) |
| customerEmail | string | Customer email — stored in the transaction (optional) |
curl -X POST https://api.moncashconnect.com/v1/pay-create \
-H "Authorization: Bearer sk_proj_xxxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 500,
"referenceId": "order_12345",
"returnUrl": "https://votresite.com/merci",
"customerName": "Jean Dupont",
"customerEmail": "jean@example.com"
}'Response (200)
{
"paymentUrl": "https://pay.moncashconnect.com/c/abc123...",
"reference": "order_12345",
"expiresAt": "2026-05-05T15:30:00.000Z"
}referenceId must be unique per project. Reusing an existing referenceId returns 409 Conflict.Safe retries with Idempotency-Key
A /pay-create request that fails (network error, timeout, 502 gateway) may have already created the transaction. To make retries safe, add the Idempotency-Key header with a string that is unique per logical attempt (UUID, order hash, etc.): a second call with the same key and the same body returns the original payment URL instead of creating a duplicate.
| Nom / Code | Type / Limite | Description |
|---|---|---|
| Idempotency-Key (header) | printable ASCII, ≤ 200 chars | Stable merchant-side identifier. Validity: 15 min (TTL window of the payment URL). |
Possible responses with Idempotency-Key
| Nom / Code | Type / Limite | Description |
|---|---|---|
| 200 | same key + same body | Original response returned (header Idempotent-Replay: true). Identical paymentUrl. |
| 422 | idempotency_key_mismatch | Same key, different body — refused to avoid a silently modified transaction. |
| 409 | idempotent_request_in_progress | A previous request with this key is still in progress. Header Retry-After: 2 — retry. |
| 502 | idempotent_replay_failed | The previous request with this key failed — use a new key to retry. |
| 410 | idempotent_replay_expired | The cached URL is > 15 min old; use a new Idempotency-Key (the previous one has expired). |
curl -X POST https://api.moncashconnect.com/v1/pay-create \
-H "Authorization: Bearer sk_proj_xxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order_12345-attempt-1" \
-d '{ "amount": 500, "referenceId": "order_12345", "returnUrl": "https://votresite.com/merci" }'Idempotency-Key (compatible with the earlier behavior): if you receive a 409 or a network failure, call GET /pay-status?referenceId=… to verify that a transaction exists before generating a new referenceId. The fallback code below remains valid for legacy integrations.Fallback without Idempotency-Key: recovery via /pay-status
General rule: poll /pay-status before generating a new referenceId. Only retry /pay-create with a fresh identifier if /pay-status returns 404 (transaction never created).
Check status
https://api.moncashconnect.com/v1/pay-status?referenceId=order_12345Query the status of a transaction by its referenceId. Useful after the customer redirect or for fallback polling if the webhook is not configured.
curl -H "Authorization: Bearer sk_proj_xxxx" \
"https://api.moncashconnect.com/v1/pay-status?referenceId=order_12345"Response (200)
{
"reference": "order_12345",
"status": "completed",
"amount": 500,
"netAmount": 485,
"completedAt": "2026-05-05T14:21:18.000Z",
"failedAt": null,
"failureReason": null,
"moncashTransactionId": "MC-123456789",
"createdAt": "2026-05-05T14:05:00.000Z"
}| Nom / Code | Type / Limite | Description |
|---|---|---|
| status | string | pending · completed · failed |
| amount | integer (HTG) | Gross amount collected |
| netAmount | integer (HTG) | Amount after commission deduction (e.g. 500 × 97% = 485 HTG on the Free plan) |
| moncashTransactionId | string | null | MonCash transaction ID — available after confirmation |
| failureReason | string | null | Failure reason if status = failed |
Balance
https://api.moncashconnect.com/v1/pay-balanceReturns the HTG balance of your MonCashConnect merchant account — that is, the total of accepted payments that have settled, minus withdrawals already made. This endpoint returns no information about any particular customer or payment; it is used to automate withdrawals to MonCash from your dashboard.
curl -H "Authorization: Bearer sk_proj_xxxx" \
"https://api.moncashconnect.com/v1/pay-balance"{
"balanceHtg": 12500,
"withdrawableHtg": 5000,
"dailyCapHtg": 5000,
"usedTodayHtg": 0
}| Nom / Code | Type / Limite | Description |
|---|---|---|
| balanceHtg | integer | Total balance available in the account |
| withdrawableHtg | integer | Amount withdrawable now (min(balance, cap - used)) |
| dailyCapHtg | integer | Daily withdrawal cap based on your plan |
| usedTodayHtg | integer | Already withdrawn today |
Withdrawals are requested from the Withdrawals page of the dashboard and are approved before being sent to MonCash.
Payouts (outbound)
There are several ways to send money from MonCashConnect. Choose the right one for your needs:
| You want to… | Route | How |
|---|---|---|
| Pay your own users (marketplace, wallet, app) | payout-create | Self-serve API, sk_proj_ key, as soon as KYC is verified. Your users live in your database; MCC only sees one account, yours. |
| Withdraw your balance to your own number, automatically | Auto withdrawals | A toggle on the Withdrawals page, no code. Details below. |
| Pay several numbers at once (CSV import) | Bulk payout | /payouts/new page. Enabled by the team. |
| An external app pays from other people's MCC accounts | external-payout-create | Partner / Connect, OAuth token. Typical HaiPay case, restricted and enabled by the team. See the Partners docs. |
| Manual withdrawal to your own number | Withdrawals page | One click from /withdrawals. |
payout-create moves your money to your users (self-serve, below). Partner/Connect (external-payout-create) lets an external app act on the balance of other MCC account holders, with their OAuth consent (restricted, enabled by the team). If you are paying your own users, you want payout-create, not Connect.Automate payments to your users (API)
https://api.moncashconnect.com/v1/payout-createSends funds from your merchant balance to any MonCash number. Your MonCashConnect account acts as a shared pool: you collect payments from your users, then redistribute to each of them — manage their individual balance in your own database.
amount you specify. Network fees are charged on top of that from your balance (see the fee_htg field in the response).Parameters (JSON body)
| Nom / Code | Type / Limite | Description |
|---|---|---|
| amount* | integer | Amount in HTG to send to the recipient, an integer between 1 and 100,000 per payout. The recipient receives this exact amount; fees are charged on top of that from your balance. |
| moncashNumber | string | Recipient's MonCash number (raw, e.g. 50912345678). Required if recipientCode is absent. |
| recipientCode | string | Code of a recipient saved in your project. Alternative to moncashNumber. |
| referenceId | string | Your idempotency identifier [a-zA-Z0-9-_], 100 characters max. A duplicate returns 409 duplicate_reference. |
curl -X POST https://api.moncashconnect.com/v1/payout-create \
-H "Authorization: Bearer sk_proj_xxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: payout-user42-2026-06-24" \
-d '{
"amount": 5000,
"moncashNumber": "50912345678",
"referenceId": "payout-user42-2026-06-24"
}'Response (201)
{
"status": "success",
"payout": {
"reference": "wd_123",
"status": "queued",
"amount_htg": 5000,
"fee_htg": 250,
"net_htg": 5000,
"recipient_account_masked": "509****1234"
}
}| Nom / Code | Type / Limite | Description |
|---|---|---|
| payout.reference | string | Internal payout identifier (wd_ prefix) |
| payout.status | string | Initial state: queued. Full cycle: queued → processing → completed | failed |
| payout.amount_htg | integer | Requested amount (what the recipient receives) |
| payout.fee_htg | integer | Network fees charged on top of that from your balance |
| payout.net_htg | integer | Amount actually received by the recipient (= amount_htg) |
| payout.recipient_account_masked | string | Recipient's masked MonCash number, for safe display |
Idempotency
Pass a unique referenceId or the Idempotency-Key header to make retries safe. A second call with the same identifier returns 409 duplicate_reference instead of creating a duplicate.
Confirmation webhook
MonCashConnect sends an HMAC-SHA256-signed POST to your webhook URL as soon as the payout is settled. The signature is verified exactly as for pay-in webhooks (X-MCC-Signature: sha256=…, X-MCC-Timestamp).
{
"event": "payout.completed",
"reference": "wd_123",
"amount": 5000,
"status": "completed",
"recipient_account_masked": "509****1234",
"completedAt": "2026-06-24T16:21:18.000Z",
"failureReason": null
}{
"event": "payout.failed",
"reference": "wd_124",
"amount": 5000,
"status": "failed",
"recipient_account_masked": "509****5678",
"completedAt": null,
"failureReason": "recipient_account_not_found"
}Payout-specific error codes
| Nom / Code | Type / Limite | Description |
|---|---|---|
| missing_bearer_token | 401 | Authorization header absent |
| invalid_key_type | 401 | The provided key is not a project key sk_proj_… |
| invalid_api_key | 401 | Invalid or revoked key |
| kyc_required | 403 | Identity verification required before enabling payouts |
| account_suspended | 403 | Merchant account suspended |
| invalid_amount | 400 | The amount is not a valid integer |
| invalid_moncash_number | 400 | Incorrect MonCash number format |
| recipient_required | 400 | Neither moncashNumber nor recipientCode provided |
| invalid_reference | 400 | referenceId does not match the format [a-zA-Z0-9-_] / exceeds 100 characters |
| insufficient_balance | 402 | Insufficient balance to cover amount + network fees |
| below_minimum | 422 | Amount below the allowed minimum |
| above_maximum | 422 | Amount above 100,000 HTG per payout |
| recipient_not_found | 404 | recipientCode not found in your project |
| duplicate_reference | 409 | referenceId or Idempotency-Key already used — the payout already exists |
Automatic withdrawals (no code)
No coding needed to automate your own withdrawals. On the Withdrawals page, enable automatic payments: choose a saved withdrawal number, set a "Send when balance reaches" threshold (minimum 100 HTG), and an optional cap per payment. As soon as your balance crosses the threshold, MonCashConnect sends automatically to that number, with a minimum delay of 30 minutes between two sends. Same network fees as for a manual withdrawal.
payout-create API above instead.Webhooks
Configure a webhook URL in your project to receive payment events in real time. MonCashConnect sends an HMAC-SHA256-signed POST to your URL as soon as a payment is finalized by MonCash.
external_payout.*) use a distinct signature scheme (t=…,v1=…) with a deduplication header X-MCC-Event-Id. See the partner documentation →Headers sent
| Nom / Code | Type / Limite | Description |
|---|---|---|
| X-MCC-Signature | string | sha256=<hex_hmac> — HMAC-SHA256 of the raw JSON body, signed with your webhook secret |
| X-MCC-Timestamp | string | Unix timestamp (seconds) of the send — reject if the gap is > 5 min |
| Content-Type | string | application/json |
Events
{
"event": "payment.completed",
"reference": "order_12345",
"amount": 500,
"status": "completed",
"completedAt": "2026-05-05T14:21:18.000Z"
}{
"event": "payment.failed",
"reference": "order_12345",
"amount": 500,
"status": "failed",
"completedAt": null
}Signature verification
Read the raw body (req.body as a Buffer) before any JSON.parse(). Reject any request whose signature is invalid or whose timestamp is more than 5 minutes old.
# Generate a test signature locally
SECRET="whsec_your_webhook_secret"
BODY='{"event":"payment.completed","reference":"test_001","amount":500,"status":"completed","completedAt":"2026-05-05T14:21:18.000Z"}'
TS=$(date +%s)
SIG="sha256=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/.*= //')"
curl -X POST https://yoursite.com/webhook \
-H "Content-Type: application/json" \
-H "X-MCC-Signature: $SIG" \
-H "X-MCC-Timestamp: $TS" \
-d "$BODY"Retry policy
If your endpoint does not respond with a 2xx within 10 seconds, MonCashConnect records the failure and schedules a new attempt after 60 seconds. Make sure your handler is idempotent — it may receive the same event several times.
Official SDKs
Official SDKs cover the three main languages. Each SDK includes error handling, HMAC verification of webhooks, and TypeScript types / Python annotations.
Node.js / TypeScript
@moncashconnect/sdkimport { MonCashClient } from "@moncashconnect/sdk";
const client = new MonCashClient(
process.env.MCC_SECRET_KEY
);
const payment = await client.createPayment(
500, "order_001",
{ returnUrl: "https://site.ht/merci" }
);
res.redirect(payment.paymentUrl);Python / pip
moncashconnectfrom moncashconnect import MonCashClient
import os
client = MonCashClient(
os.environ["MCC_SECRET_KEY"]
)
payment = client.create_payment(
500, "order_001",
return_url="https://site.ht/merci"
)
# redirect(payment["paymentUrl"])PHP / Composer
moncashconnect/php-sdkuse MonCashConnect\Client;
$client = new Client(
$_ENV['MCC_SECRET_KEY']
);
$payment = $client->createPayment(
500, 'order_001',
['returnUrl' => 'https://site.ht/merci']
);
header('Location: '.$payment['paymentUrl']);Guides by framework
Fees
MonCashConnect takes 0% on your transactions.
The fees you see on the gateway come from the Digicel MonCash network. We pass them through unchanged — no added markup.
Detailed fees
| Nom / Code | Type / Limite | Description |
|---|---|---|
| Collection | 2.9% | Charged on every incoming payment via MonCash. On 1,000 HTG collected, you receive 971 HTG. |
| MonCash withdrawal | 5% | Charged when sending to a MonCash number. To send 1,000 HTG, your balance decreases by 1,050 HTG. |
| Transfers between MCC accounts | 0% | Free — no network fees, no MCC commission. |
| MonCashConnect commission | 0% | We take nothing on transactions, regardless of the plan. |
So why does a subscription exist?
The subscription (Pro, Early Visionary Pass) unlocks only two things: more than one API project on your account, and higher daily caps on withdrawals. As long as you stay on a single project and the Free cap, you have no reason to pay. See the plans →
Rate limits
Limits apply per project secret key and per IP address. Exceeding them returns a 429 Too Many Requests.
| Nom / Code | Type / Limite | Description |
|---|---|---|
| POST /pay-create | 60 / min / key | 30 / min / IP |
| GET /pay-status | 120 / min / key | 60 / min / IP |
| GET /pay-balance | 120 / min / key | 60 / min / IP |
In case of repeated overruns, implement an exponential backoff with jitter between attempts. Avoid intensive polling on pay-status — prefer webhooks for real-time confirmations.
Hosted pages (deposit QR & invoices)
The payment pages hosted by MonCashConnect — the QR-code deposit page and the invoice settlement page — are loaded by your customer's browser, not by your server. They have their own limits, applied per IP address and per public token; exceeding them also returns a 429 Too Many Requests with a Retry-After: 60 header.
| Nom / Code | Type / Limite | Description |
|---|---|---|
| QR deposit page (creation) | 20 / min / IP | 30 / min / token |
| QR deposit — payer info | 120 / min / IP | Public read (display) |
| QR deposit — status | 120 / min / IP | Confirmation page polling |
| Invoice page (payment) | 20 / min / IP | 30 / min / token |
| Invoice — info | 120 / min / IP | Public read (display) |
These limits protect the hosted pages against abuse; they do not affect your server calls authenticated with an sk_proj_ key.
Error codes
All errors return a JSON object { "error": "message" }.
| Nom / Code | Type / Limite | Description |
|---|---|---|
| 400 | Bad Request | Invalid or missing parameter — check amount, referenceId, returnUrl |
| 401 | Unauthorized | API key absent, invalid, or revoked |
| 403 | Forbidden | Origin not allowed (CORS) or account suspended |
| 404 | Not Found | Transaction not found for this referenceId and project |
| 409 | Conflict | referenceId already used for this project — use a unique identifier |
| 429 | Too Many Requests | Rate limit reached — wait before retrying (exponential backoff) |
| 500 | Internal Error | Internal error — contact support if the problem persists |
| 502 | Bad Gateway | Payment gateway error — retry in a few seconds |
Structured error codes (400 response)
Beyond HTTP codes, some 400 responses include a machine-readable code field. Use it to branch your client-side logic.
| Nom / Code | Type / Limite | Description |
|---|---|---|
| invalid_request | 400 | Missing or malformed parameter (amount, referenceId, returnUrl, etc.) |
| amount_below_minimum | 400 | Amount below the minimum (1 HTG for creation, 1,000 HTG for withdrawal) |
| invalid_token | 401 | Invalid / expired OAuth token or API key (Connect integrations) |
| insufficient_scope | 403 | The token lacks the required scope (balance.read, payout.request, etc.) |
| connection_revoked | 403 | The merchant revoked your application's access (Connect integrations) |
| connection_not_found | 404 | No active connection between your application and this merchant |
| insufficient_balance | 402 | Insufficient merchant balance for the requested withdrawal |
| partner_daily_cap_exceeded | 429 | Your application's daily cap reached (Connect integrations) |
| plan_daily_cap_exceeded | 429 | The merchant plan's daily cap reached |
| rate_limit_exceeded | 429 | Rate limit exceeded — wait and retry with backoff |
| auth_unavailable | 503 | Authentication service temporarily unavailable — retry |
| server_misconfigured | 500 | Configuration error on the MonCashConnect side — contact support |
| not_found | 404 | Resource (transaction, payout, connection) not found |
Example error response
{
"error": "referenceId already exists for this project",
"code": "duplicate_reference"
}The code field is stable; the error field is meant for humans and may be translated or reworded.
Related guides