MolnPayDocs
Guides

Going live

Key hygiene, the IP allowlist, signing, and the checklist to walk before real money moves.

A test project (mptest… keys) rehearses everything with the chain simulated — deposits you trigger, payouts that settle on their own — and lists native coins only; see Test mode for exactly what differs. A live project moves real money on the first call, so the list below is worth walking rather than skimming.

Key hygiene

Your API key is a bearer credential. Anyone holding it can act as your project within the key's kind and scopes.

  • Server-side only. Never in a browser bundle, a mobile app binary, a public repository, a CI log or a support ticket. A key that has ever been in any of those is compromised regardless of what happened since.
  • Shown once. We store a peppered hash, never the value. Only the display handle (mp3f9c1a77) and the last four hex are kept, to tell keys apart.
  • Roll by overlap. POST /v1/api_keys/{id}/roll (or the console) mints a replacement with identical grants and gives the old key a grace window, 24 hours by default. Deploy the new key, confirm traffic is flowing on it, and let the old one expire.
  • One key per environment, one per system. Separate keys mean you can revoke one without touching the others, and the handle in your logs says which system made a call.
  • Revoke on suspicion, not on proof. Revocation is instant and takes effect within a minute; minting a replacement takes seconds.

Keys are managed by the signed-in owner in the console, never with a key: a leaked key cannot mint further keys, so its blast radius stays what that key could do.

Kind, scope, signing

Three fences, checked in that order:

  1. Kindcheckout, wallet or payout — decides which routes a key may reach at all. The key your storefront carries cannot send funds anywhere.
  2. Scopes narrow it further. A key without payouts:create cannot create a payout whatever its kind.
  3. Signing is the third factor for money out: it must be on for the service, for your project and for that key. A payout key with signing off can read and prepare but never send. GET /v1/ping says which of the three is blocking in signing_blocked_by.

Pinning a key to IP addresses

Any key can be restricted to a list of addresses — exact IPs or CIDR blocks, v4 or v6, up to 32 — in the console or with PATCH /v1/api_keys/{id}.

  • The address judged is the one your server presents to us.
  • Empty means no restriction; the first entry makes the list exclusive.
  • An entry that does not parse is refused at save time, never stored.
  • A refused call answers 403 ip_not_allowed.

Pin only where the egress address is stable

Functions on shared infrastructure often present a different egress address on every call, and a pinned key would fail there intermittently. Pin the keys held by servers (or a NAT gateway) with addresses you control; leave a key on unpredictable egress unpinned and rely on kind and scope.

Webhooks before the first payment

  • Register the endpoint, send a test event, and watch the delivery succeed — Manage endpoints.
  • Verify the signature over the raw body before trusting any field — Verify a signature.
  • Dedupe on event.id; order on sequence; return 2xx within 10 seconds and process afterwards.
  • Fulfil on deposit.confirmed (or invoice.paid), never on deposit.detected, and reverse on deposit.orphaned.

The checklist

Amounts stay strings through your whole stack. Search your code for Number( and parseFloat( near anything that came from the API.

Decimals come from GET /v1/chains, per asset per chain. The BSC 18-decimal case is the one to test.

Idempotency-Key on every POST /v1/payouts, generated per intent and stored with the intent, so a retry after a timeout sends the same key.

The webhook endpoint verifies, dedupes and acks fast, and your handler has a default branch for event types it does not know.

The payout key is pinned and held only by your backend, and signing is on at all three levels — check GET /v1/ping.

Reconcile against the REST API, not against the events you happened to receive. If an event and a GET disagree, the GET is right.

On this page