Developers

An API that respects your afternoon.

One call starts a checkout. One script opens it over your site. Signed webhooks tell you the rest. Everything works in test mode with sandbox MoMo before a single cedi moves.

// Your server: start a checkout for a plan
const res = await fetch('https://api.usemonthly.app/v1/checkout-sessions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.MONTHLY_KEY}`, // mk_test_…
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    planId: '7c1f…',
    successUrl: 'https://streamgh.com/welcome?session={CHECKOUT_SESSION_ID}',
    cancelUrl: 'https://streamgh.com/',
    clientReferenceId: user.id,
  }),
});

const { url } = await res.json();
// Send the customer to url, or open it in the pop-up
Quickstart

From key to first subscriber.

Amounts are whole pesewas, lists are newest first, and every object belongs to test or live mode. A key only ever sees its own mode.

  1. 01

    Get a test key

    Sign up, and create an mk_test_ key under Developers. It’s shown once; we only keep its hash.

  2. 02

    Create a plan

    In the dashboard or with the API: a name, a price in pesewas, a cycle, and an optional trial.

  3. 03

    Start a checkout

    Your server creates a checkout session and gets back a url, valid for 24 hours.

  4. 04

    Listen for events

    Add a webhook endpoint, check the signature, and act on each event once.

Two ways to check out

Redirect, or keep them on your page.

Both use the same session. Redirect to its url, or load monthly.js and call Monthly.openCheckout. It becomes a bottom sheet on phones.

Sealed off from your page

The sheet lives in a closed shadow root and a sandboxed iframe: your CSS can’t break it, and it can’t navigate your page.

Only on your site

The checkout shows nothing in a frame until your page answers a handshake from the origin of your successUrl or cancelUrl. Nobody else can frame it.

Messages go to one origin

After the handshake, checkout talks only to your origin, and the SDK only listens to its own iframe.

Accessible

A real modal: focus moves in and comes back, Escape closes it, and the page behind doesn’t scroll.

Webhooks

Signed, retried, never lost.

Each delivery carries Monthly-Signature: an HMAC-SHA256 of the timestamp and raw body, keyed with your endpoint’s whsec_ secret. Anything but a 2xx is retried 8 more times over about three days.

checkout.session.completedA customer paid, or started a trial, through checkout. Carries your clientReferenceId.
subscription.trial_startedA plan with a trial began. Nothing was charged.
subscription.activatedThe first payment went through.
subscription.renewedA renewal or a retry was paid.
subscription.payment_failedA charge was declined. Retries are scheduled.
subscription.pausedThe last retry failed, or the customer paused.
subscription.resumedBack on, after a pause.
subscription.cancelledEnded, now or at the end of the paid period.
Retry schedule
  1. now
  2. 1m
  3. 5m
  4. 30m
  5. 2h
  6. 6h
  7. 12h
  8. 24h
  9. 24h

Delivery is at least once and can be out of order: deduplicate on the event id. Monthly never calls private addresses, and live endpoints must use https.

The API

The routes you’ll use most.

Send Authorization: Bearer mk_test_… from your server. Never from a browser: secret keys stay secret.

POST/v1/plansCreate a plan
POST/v1/checkout-sessionsStart a checkout, get its url
GET/v1/subscriptionsSubscribers, filtered by status or plan
POST/v1/subscriptions/:id/cancelCancel now or at period end
GET/v1/subscriptions/:id/paymentsEvery charge attempt, with Monthly’s fee
POST/v1/payments/:id/refundsRefund some or all, to credits
GET/v1/eventsEverything that happened, to catch up
GET/v1/reports/paymentsSuccessful payments per day
Sandbox

Break things in test mode.

Test mode never moves real money. Use these to walk every path, including the whole failed-payment journey.

A MoMo number ending in 0000Declined, every time
Any other MoMo numberApproved after about a second
A bank account starting with failDeclined
Test creditsBought through the sandbox; pay only for test subscriptions

Your first test subscriber, today.

Create a test key in a couple of minutes. Nothing to install on your server but a fetch call.