MONEI Connect
Become an integrated payments partner to provide payment services to your platform or marketplace users with MONEI Connect. Onboard merchants under your brand, operate their accounts on their behalf via the REST and GraphQL APIs, and embed in-person NFC card payments directly inside your merchant app with the MONEI Pay SDKs.
Before you begin
Contact connect@monei.com to register as a partner. Include:
- Your business — legal name, website, Tax Identification Number (CIF), services description.
- First admin user — name, email, phone number. You can add more users later from the dashboard.
- IPs of your servers (optional but recommended) so we can restrict API calls to your infrastructure.
Once provisioned, you get access to the Partner Dashboard at admin.monei.com and a registration link unique to your partner account. Every merchant who signs up via this link is bound to you.
How it works
API keys
Generate your Partner API Keys at admin.monei.com/settings/api → Create API Key. The page is mode-aware: toggle to live mode for your pk_live_… key, test mode for your pk_test_… key. Keys can be revoked and re-created from the same page.
Test mode and live mode
Every partner account is really a pair: a live account (pk_live_…) and a test account (pk_test_…) permanently linked together. Every merchant who signs up through your registration link gets the same twin treatment — one prod sub-account and one test sub-account with different IDs.
This means you can build and test the entire integration — onboarding, API calls, webhooks — in test mode before any merchant completes contract signing.
- Test sub-accounts are active immediately after sign-up email confirmation; the merchant does not need to finish onboarding.
- Live sub-accounts stay
PENDING_CONTRACTuntil the merchant signs the contract and is approved. - Live keys cannot call test sub-accounts (and vice versa). Mode mismatch is the #1 source of 401 errors — see Common authentication failures.
- The mode toggle in the dashboard (top-nav) swaps the entire view between live and test sub-accounts.
Registration link
The registration link is unique to your partner account. Every merchant who registers through it is bound to you.
https://dashboard.monei.com/?action=signUp&promo=<PARTNER_CODE>
Optionally pass an external ID to identify the merchant in your own system. The ID is echoed back on every webhook.
https://dashboard.monei.com/?action=signUp&promo=<PARTNER_CODE>&mid=<EXTERNAL_ID>&h=<HASH>
mid— your external merchant ID.h—HMAC-SHA256(<EXTERNAL_ID>, <PARTNER_API_KEY>)in hex.
Your partner account can be configured to require an external ID. When enabled, the registration link must always include mid + h.
Integration
Send your Partner API Key in the Authorization header and the merchant's account ID in the MONEI-Account-ID header on every request.
REST API
Full reference: REST API.
- cURL
- Node.js
- PHP
- Python
curl --request POST 'https://api.monei.com/v1/payments' \
--header 'Authorization: <PARTNER_API_KEY>' \
--header 'MONEI-Account-ID: <MONEI_ACCOUNT_ID>' \
--header 'Content-Type: application/json' \
--header 'User-Agent: MONEI/<PARTNER_NAME>/0.1.0' \
--data-raw '{
"amount": 110,
"currency": "EUR",
"orderId": "14379133960355",
"description": "Test Shop - #14379133960355",
"customer": {
"email": "email@example.com"
},
"callbackUrl": "https://example.com/checkout/callback"
}'
import {Monei} from '@monei-js/node-sdk';
// Initialize with Partner API Key, Account ID, and User-Agent
const monei = new Monei('<PARTNER_API_KEY>', {
accountId: '<MONEI_ACCOUNT_ID>',
userAgent: 'MONEI/<PARTNER_NAME>/0.1.0'
});
async function createMerchantPayment() {
try {
const payment = await monei.payments.create({
amount: 110,
currency: 'EUR',
orderId: '14379133960355',
description: 'Test Shop - #14379133960355',
customer: {
email: 'email@example.com'
},
callbackUrl: 'https://example.com/checkout/callback'
});
console.log('Payment created:', payment.id);
// Handle the payment response (e.g., redirect URL)
} catch (error) {
console.error('Error creating payment:', error.message);
}
}
createMerchantPayment();
<?php
require_once 'vendor/autoload.php';
use Monei\MoneiClient;
use Monei\Model\CreatePaymentRequest;
use Monei\Model\PaymentCustomer;
// Initialize with Partner API Key
$monei = new MoneiClient('<PARTNER_API_KEY>');
// Set Account ID and User-Agent for the specific merchant
$monei->setAccountId('<MONEI_ACCOUNT_ID>');
$monei->setUserAgent('MONEI/<PARTNER_NAME>/0.1.0');
try {
$payment = $monei->payments->create(
new CreatePaymentRequest([
'amount' => 110,
'currency' => 'EUR',
'order_id' => '14379133960355',
'description' => 'Test Shop - #14379133960355',
'customer' => new PaymentCustomer([
'email' => 'email@example.com'
]),
'callback_url' => 'https://example.com/checkout/callback'
])
);
echo 'Payment created: ' . $payment->getId();
// Handle the payment response (e.g., redirect URL)
} catch (\Exception $e) {
echo 'Error creating payment: ' . $e->getMessage();
}
?>
import Monei
from Monei import CreatePaymentRequest, PaymentCustomer
# Initialize with Partner API Key
monei = Monei.MoneiClient(api_key='<PARTNER_API_KEY>')
# Set Account ID and User-Agent for the specific merchant
monei.set_account_id('<MONEI_ACCOUNT_ID>')
monei.set_user_agent('MONEI/<PARTNER_NAME>/0.1.0')
try:
payment = monei.payments.create(
CreatePaymentRequest(
amount=110,
currency="EUR",
order_id="14379133960355",
description="Test Shop - #14379133960355",
customer=PaymentCustomer(
email="email@example.com"
),
callback_url="https://example.com/checkout/callback"
)
)
print(f'Payment created: {payment.id}')
# Handle the payment response (e.g., redirect URL)
except Exception as e:
print(f'Error creating payment: {e}')
GraphQL API
Full reference: GraphQL API.
curl --request POST 'https://graphql.monei.com' \
--header 'Authorization: <PARTNER_API_KEY>' \
--header 'MONEI-Account-ID: <MONEI_ACCOUNT_ID>' \
--header 'Content-Type: application/json' \
--header 'User-Agent: MONEI/<PARTNER_NAME>/0.1.0' \
--data-raw '{"query":"{account {name status}}"}'
In-person NFC payments
Full reference: MONEI Pay SDK.
Accept Tap-to-Pay card payments directly inside your own merchant app, on iOS, Android, React Native, or Web. Your backend generates a short-lived POS auth token for the merchant, then hands it to the SDK on the merchant's device.
- cURL
- Node.js
- PHP
- Python
curl --request POST 'https://api.monei.com/v1/pos/auth-token' \
--header 'Authorization: <PARTNER_API_KEY>' \
--header 'MONEI-Account-ID: <MONEI_ACCOUNT_ID>' \
--header 'User-Agent: MONEI/<PARTNER_NAME>/0.1.0'
import {Monei} from '@monei-js/node-sdk';
const monei = new Monei('<PARTNER_API_KEY>', {
accountId: '<MONEI_ACCOUNT_ID>',
userAgent: 'MONEI/<PARTNER_NAME>/0.1.0'
});
const {token} = await monei.posAuthToken.create();
// Send `token` to the merchant app
<?php
require_once 'vendor/autoload.php';
use Monei\MoneiClient;
$monei = new MoneiClient('<PARTNER_API_KEY>');
$monei->setAccountId('<MONEI_ACCOUNT_ID>');
$monei->setUserAgent('MONEI/<PARTNER_NAME>/0.1.0');
$result = $monei->posAuthToken->create();
$token = $result->getToken();
// Send $token to the merchant app
?>
import Monei
monei = Monei.MoneiClient(api_key='<PARTNER_API_KEY>')
monei.set_account_id('<MONEI_ACCOUNT_ID>')
monei.set_user_agent('MONEI/<PARTNER_NAME>/0.1.0')
result = monei.pos_auth_token.create()
token = result.token
# Send token to the merchant app
The response contains a JWT valid for 24 hours, scoped to the merchant identified by MONEI-Account-ID. Pass it to the SDK on the merchant's device. Optionally pass pointOfSaleId / storeId in the body to scope the token to a specific terminal or store.
Refunds must never be automated. Each refund requires a written, traceable instruction from the merchant. Partners are liable for any unauthorized refund and may have their contract terminated.
Test the integration
You can build the full partner integration end-to-end before a single merchant signs a contract.
- Register a test merchant via your registration link:
https://dashboard.monei.com/?action=signUp&promo=<PARTNER_CODE>. No need to complete onboarding — the test sub-account is already active. - Get the test sub-account ID: in the merchant dashboard, toggle test mode (top-nav switch) → Settings → copy the Account ID.
- Get your test partner API key: log in to admin.monei.com → toggle test mode → Settings → API Access → click Create API Key if you don't have one yet → copy the
pk_test_*key. - Call the API with both headers, using test card numbers and response simulators documented in Testing:
curl --request POST 'https://api.monei.com/v1/payments' \
--header 'Authorization: pk_test_<...>' \
--header 'MONEI-Account-ID: <TEST_SUB_ACCOUNT_ID>' \
--header 'Content-Type: application/json' \
--data-raw '{"amount": 110, "currency": "EUR", "orderId": "test-1"}'
Common authentication failures
When you get a 401, check these in order:
- Mode mismatch — using a
pk_live_*key with a testMONEI-Account-ID(or vice versa). Partner key and sub-account must be in the same mode. - IP not allowlisted — if you registered an IP allowlist (optional but recommended), calls must originate from one of those IPs. To add or remove an IP, email connect@monei.com.
- Wrong
MONEI-Account-ID— the sub-account must belong to your partner account. Sub-accounts of other partners (or standalone accounts) are rejected. - Missing
MONEI-Account-ID— without it, the key authenticates as the partner itself, which only works for a small subset of partner-only endpoints. For all merchant operations, include the header.
Webhooks
Configure webhooks in the Partner Dashboard at Settings → Webhooks. Each webhook is an endpoint URL plus the subset of event types you want to receive.
Webhooks on your partner account receive a fan-out of events from every merchant bound to it — no per-sub-account configuration needed. The payload's accountId identifies the merchant.
Subscribing to events
Common partner subscriptions:
| Group | Useful events |
|---|---|
| Merchant lifecycle | account.pending, account.approved, account.rejected, account.activated, account.suspended, account.blocked, account.unblocked |
| Payouts | account.iban_updated, account.payout_iban_updated, account.payouts_resumed, account.payouts_suspended, settlement.completed, settlement.pending |
| Payments (per merchant) | charge.succeeded, charge.failed, charge.refunded, charge.chargeback, refund.succeeded |
| Subscriptions (per merchant) | subscription.activated, subscription.canceled, subscription.past_due |
| Billing | account_invoice.paid, account_invoice.past_due |
The full catalog is available in the dashboard's event-type picker when you add or edit a webhook.
Payload
MONEI POSTs application/json with this envelope on every event:
| Field | Type | Description |
|---|---|---|
| id | string | Event ID. |
| type | string | Event type (e.g. charge.succeeded). |
| objectType | string | One of charge, subscription, account, account_invoice, settlement, provider. |
| objectId | string | ID of the affected object. |
| accountId | string | Merchant sub-account the event belongs to. Use this to route events to the right merchant in your system. |
| livemode | boolean | true for live events, false for test events. Match this to the API key you use to verify the signature. |
| createdAt | number | Event timestamp, unix seconds. |
| object | object | The affected object. Shape depends on objectType — see tabs below. |
- charge
- subscription
- account
- account_invoice
object is a Payment — full field reference at Payment schema.
{
"id": "evt_5fda0f3b7b2f4e0d…",
"type": "charge.succeeded",
"objectType": "charge",
"objectId": "ch_8e2f…",
"accountId": "<MERCHANT_SUB_ACCOUNT_ID>",
"livemode": true,
"createdAt": 1748390400,
"object": {
"id": "ch_8e2f…",
"amount": 1099,
"currency": "EUR",
"status": "SUCCEEDED",
"orderId": "ORDER-4421",
"customer": {"email": "buyer@example.com"},
"paymentMethod": {"method": "card", "card": {"brand": "VISA", "last4": "4242"}}
}
}
object is a Subscription — full field reference at Subscription schema.
{
"id": "evt_…",
"type": "subscription.activated",
"objectType": "subscription",
"objectId": "sub_…",
"accountId": "<MERCHANT_SUB_ACCOUNT_ID>",
"livemode": true,
"createdAt": 1748390400,
"object": {
"id": "sub_…",
"amount": 999,
"currency": "EUR",
"interval": "month",
"intervalCount": 1,
"status": "ACTIVE",
"nextPaymentAt": 1751068800,
"customer": {"email": "buyer@example.com"}
}
}
object is the affected merchant account. Fired by every account.* and master_account.* event type.
| Field | Type | Description |
|---|---|---|
| id | string | Sub-account ID. |
| name | string | Trading name. |
| status | string | PENDING_CONTRACT, PENDING_APPROVAL, APPROVED, ACTIVE, REJECTED, SUSPENDED, BLOCKED. |
| statusReason | string | Machine-readable reason for the current status (when applicable). |
| statusNote | string | Free-text note from MONEI Ops (when applicable). |
| test | boolean | true for the test twin sub-account. |
| adminEmail | string | Merchant admin email. |
| iban | string | Payout IBAN, obfuscated (first 4 + last 4 only). |
| externalId | string | Partner-supplied merchant identifier (if you use the External ID flow). |
| language | string | Dashboard language code. |
| billingPlan | string | e.g. MONEI_X, MONEI_PLUS. |
| blocked | boolean | true if MONEI has blocked the account. |
| churned | boolean | true if the merchant has churned. |
| churnReason / churnReasonOption / churnNote | string | Churn metadata (when churned: true). |
| contactPerson | object | {name, email, phone} of the merchant admin. |
| business | object | {legalName, legalForm, email, phone, website, country, address, documentNumber, status, servicesDescription}. |
{
"id": "evt_…",
"type": "account.approved",
"objectType": "account",
"objectId": "<MERCHANT_SUB_ACCOUNT_ID>",
"accountId": "<MERCHANT_SUB_ACCOUNT_ID>",
"livemode": true,
"createdAt": 1748390400,
"object": {
"id": "<MERCHANT_SUB_ACCOUNT_ID>",
"name": "ACME Shop",
"status": "APPROVED",
"test": false,
"adminEmail": "owner@acme.com",
"iban": "ES12********6789",
"externalId": "merchant-7421",
"language": "es",
"billingPlan": "MONEI_X",
"blocked": false,
"business": {
"legalName": "ACME COMERCIO SL",
"documentNumber": "B12345678",
"country": "ES",
"website": "https://acme.com",
"email": "info@acme.com",
"status": "active"
}
}
}
object is the merchant's MONEI invoice for accrued platform fees. Fired by account_invoice.* events.
| Field | Type | Description |
|---|---|---|
| id | string | Invoice ID. |
| number | string | Human-readable invoice number printed on the PDF. |
| accountId | string | Sub-account the invoice belongs to. |
| status | string | PENDING, PAID, PAST_DUE, UNPAID. |
| period | object | {from, to} unix seconds covering the billing period. |
| currency | string | ISO 4217 code. |
| amount | number | Net amount in minor units. |
| tax | number | Tax in minor units. |
| totalAmount | number | amount + tax, minor units. |
| lineItems | array | Per-charge line items: {name, quantity, unitType (money|percent), unitAmount, amount, currency}. |
| business | object | {legalName, documentNumber, iban (obfuscated), mandateSignatureTimestamp}. |
| createdAt | number | Invoice creation timestamp, unix seconds. |
{
"id": "evt_…",
"type": "account_invoice.paid",
"objectType": "account_invoice",
"objectId": "inv_…",
"accountId": "<MERCHANT_SUB_ACCOUNT_ID>",
"livemode": true,
"createdAt": 1748390400,
"object": {
"id": "inv_…",
"number": "1/2026-14510",
"accountId": "<MERCHANT_SUB_ACCOUNT_ID>",
"status": "PAID",
"period": {"from": 1773014400, "to": 1773619199},
"currency": "EUR",
"amount": 153,
"tax": 0,
"totalAmount": 153,
"lineItems": [
{
"name": "Flat fee per Credit card transaction",
"quantity": 1,
"unitType": "money",
"unitAmount": 24,
"amount": 24,
"currency": "EUR"
},
{
"name": "MONEI X daily platform usage fee",
"quantity": 7,
"unitType": "money",
"unitAmount": 10,
"amount": 70,
"currency": "EUR"
}
],
"business": {
"legalName": "ACME COMERCIO SL",
"documentNumber": "B12345678",
"iban": "ES93********0012"
}
}
}
Verifying the signature
Every request carries a MONEI-Signature header in the form t=<unix-seconds>,v1=<hex>. Compute HMAC-SHA256("<t>.<rawBody>", <PARTNER_API_KEY>) and compare to v1. Use the same Partner API Key (live or test) that matches the event's livemode. Full walkthrough: Verify signature.
Delivery and retries
- Reply with
2xxto acknowledge. Anything else (including3xx) is treated as a failure. - MONEI retries failed deliveries with backoff for several days, then gives up.
User-Agent: MONEI/1.0identifies our requests.
The legacy accountCallbackUrl configured during partner onboarding still works — it fires only on account.pending, account.approved, and account.rejected with a limited payload — but it is being phased out. New integrations should use the webhook configuration in the Partner Dashboard.
Manage your partner account
All partner-account management is self-service in the Partner Dashboard.
Authorized users
Invite additional team members at admin.monei.com/settings/users → Add new user. Provide their email, name, and phone number — they'll receive an invitation to set their password. All partner users are MFA-enforced.
Security
admin.monei.com/settings/security — three independent controls:
- Allowed IPs — every API call against your Partner API Key must originate from one of these IPs. Acts as a second line of defense if a key leaks: a stolen key from outside your infrastructure becomes unusable.
- Allowed domains — restricts which domains can load the hosted payment page for merchants bound to your account. Prevents your merchants' checkout from being embedded by unrelated sites.
- Require external ID — when enabled, every merchant signing up via your registration link must carry a valid
mid+hHMAC (see Registration link). Use this if you only want sign-ups that originate from your own onboarding flow, never direct visits to the link.
Branding
admin.monei.com/settings/branding — upload your logo. On the merchant sign-up page opened from your registration link, your logo appears alongside MONEI's (separated by a +), signaling the co-branded partnership. Use this so merchants recognize your platform as the source of the payments offering instead of arriving at an unbranded MONEI sign-up.
Email notifications
admin.monei.com/settings/emails — add extra recipient lists for operational emails MONEI sends about your partner account:
- Webhook emails — recipients alerted when webhook delivery to your endpoint starts failing. Route to your ops or on-call mailbox so you fix a broken endpoint before piling up missed events.
- Churned emails — recipients alerted when a merchant bound to you churns. Route to your account-management or finance team so they can follow up without polling the API.