Platform quickstart
Audience: a developer at a platform (a product like LodgePay or ChurchPay) that wants to take payments on behalf of many tenants under one credential. If you're integrating Mooov for your own business, you want the merchant quickstart instead.
A merchant holds one API key scoped to one business. A
platform holds one mk_platform_* key and acts on behalf of any
tenant who has connected to it: each tenant approves you once through
a hosted consent flow, Mooov records a grant for the
(platform, merchant) pair, and from then on you address that tenant
with a Mooov-Merchant header on ordinary API calls. No grant, no
access. The full contract is the
Connect protocol; this page is the shortest
path through it.
1. Get registered
Platform registration is operator-led. There is no self-serve signup.
Email ops@mooov.money with:
- a slug (lowercase, stable, e.g.
churchpay) and a display name (what tenants see on the consent screen) - a security contact email
- your redirect URI allowlist, one or more exact URLs per environment (exact-match only: no path prefixes, no wildcards)
- your webhook URL per environment
Mooov provisions your integration against the sandbox first and you receive:
- a platform key id (
mk_platform_*) and its secret, shared once via a 1Password link (24h TTL) - a webhook signing secret for your webhook URL
Sandbox and production are fully separate keys, grants, and webhook destinations; see Environments. You build and certify against the sandbox, then repeat the ceremony for production.
2. Sign a request
Same HMAC scheme as merchant keys, plus one header. Your secret is never sent on the wire:
X-Mooov-Key-Id: mk_platform_xxxxxxxx
X-Mooov-Timestamp: 2026-06-12T12:00:00Z (RFC3339 UTC, ±5 min skew)
X-Mooov-Signature: <lowercase hex HMAC-SHA256>
Mooov-Merchant: merch_<id> (required on on-behalf-of calls)
Idempotency-Key: <uuid> (required on writes)
The signature is HMAC-SHA256 over exactly four lines (no trailing newline):
<METHOD>\n<PATH>\n<TIMESTAMP>\n<hex SHA-256 of the raw body, or of "" on GET>
Drop-in signers in Python and TypeScript (with the Mooov-Merchant
header already wired in) are in
§2 of the Connect protocol.
3. Connect your first tenant
Five steps, OAuth-shaped:
You generate a signed
statetoken and send the tenant's browser to the hosted authorize page:https://sandbox.connect.mooov.money/authorize ?client_id=<your platform slug> &redirect_uri=<your callback URL, exact-match against your allowlist> &state=<your signed CSRF token> &scope=payments:write+payments:read+customers:writescopeis OAuth-style space-separated (a+in a query string is a space). Pick scopes from the scopes reference; request only what you need, because widening later means re-prompting the tenant.The tenant signs into Mooov (or creates an account), sees your display name and the requested scopes on a consent screen, and clicks Connect.
Mooov records the grant and redirects to
<redirect_uri>?code=<auth_code>&state=<your state>. The code is single-use and expires in 10 minutes.You verify
state, then exchange the code with an HMAC-signedPOST /v1/public/connect/token:{ "grant_type": "authorization_code", "code": "<auth_code>", "redirect_uri": "<same redirect_uri as step 1>" }You store the
merchant_id. The response is a durable identifier, not a bearer token. There is nothing to refresh and nothing that expires:{ "merchant_id": "merch_test_…", "grant_id": "grant_01HXX…", "granted_scopes": ["payments:write", "payments:read", "customers:write"], "token_type": "merchant_id" }Keep
grant_idtoo; it's the handle for disconnecting the tenant later (§5 of the Connect protocol).
4. First on-behalf-of charge
Address the tenant with Mooov-Merchant and call the same payments
API a merchant would. Hosted checkout is the fastest path:
const res = await signedPost("/v1/payment_intents", {
payment_id: `pay_${crypto.randomUUID()}`, // your stable id
amount: 4200, // minor units = £42.00
currency: "GBP",
flow: "redirect",
success_url: "https://your-platform.example/checkout/success",
cancel_url: "https://your-platform.example/checkout/cancel",
description: "Dues 2026",
}); // Mooov-Merchant: merch_test_…
const { provider } = await res.json();
// → send the tenant's customer to provider.hosted_url
In the sandbox, pay with the test card 4242 4242 4242 4242 (any future
expiry, any CVC); decline and 3DS cards are on the
sandbox page. A 403 here means the grant is missing or
revoked, or the action isn't in the granted scopes; the error code
table is in §7 of the Connect protocol.
5. Verify webhooks
You register one webhook URL per environment (part of the ceremony in step 1) and it receives signed events for all your connected tenants:
X-Mooov-Signature: t=<unix>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
X-Mooov-Delivery: 42 (numeric delivery id, dedupe key)
Verify the signature in constant time before trusting the body. After
the test charge above you'll receive payment.succeeded within a
couple of seconds. Verification code samples are in
§6 of the Connect protocol; the full event list
is in the webhook event catalog.
6. Go live
- Run the sandbox go-live checklist, including the Connect-specific grant-revocation item.
- Repeat the credential ceremony with
ops@mooov.moneyfor production. Production keys, grants, and webhook secrets are separate from the sandbox. - Swap the base URLs:
https://api.mooov.moneyandhttps://connect.mooov.money/authorize. The API surface is identical. - Re-run the consent flow with a real tenant and make one low-value live charge before announcing. There is no test mode on production; see Environments.
7. Watch your integration (platform portal)
Your team can see your connected merchants and integration health
without calling the API: the platform portal at
https://connect.mooov.money/portal shows your merchant roster
(grants, charge-readiness, payment volume), your webhook delivery
posture (status, consecutive failures, last delivery), and your API
key metadata (never the secrets).
Access is invite-only. Ask your Mooov contact to invite your technical lead by email; they'll receive a single-use link to set a password. There is no self-signup, and portal logins are entirely separate from your API credentials — a portal account can never sign API requests.
Questions: integrations@mooov.money.