Billing
Subscriptions
Recurring billing for any product whose price has type recurring. Created the same way as a one-time checkout. Merida figures out the cadence from the price and starts invoicing on the schedule.
Create a subscription checkout
Subscriptions are minted via the same POST /checkouts call as one-off
payments. Pass a product_id whose active price has type: "recurring" and
an interval ("day", "week", "month", "year"). The buyer lands on
the hosted checkout, pays the first cycle, and a recurring schedule starts.
curl -X POST https://api.meridapay.com/checkouts \
-H "Authorization: Bearer $MERIDA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_id": "<recurring-product-uuid>",
"customer_email": "[email protected]",
"external_customer_id": "user_123",
"metadata": { "plan": "pro" }
}'
The metadata you set here lives on every subsequent invoice and webhook
event for the lifetime of the subscription. Use it to carry your internal
plan ID or any correlation key.
Lifecycle events
Webhook events tell the story of every subscription. Subscribe to all of them or pick a subset.
subscription.createdwebhookFires once when a subscription is created (checkout success, manual create, or trial start).
subscription.updatedwebhookFires on plan change, pause, resume, or scheduled cancel. The payload includes the new state and the effective timestamp.
subscription.canceledwebhookFires when a subscription is canceled (immediately or at period end).
subscription.expiredwebhookFires when a subscription reaches its end and is no longer active.
subscription.trial_endedwebhookFires when a trial completes, either converted into a paid subscription or expired.
invoice.createdwebhookFires whenever a new invoice is generated: at renewal, ad-hoc billing, or proration on plan change. Use the invoice ID to fetch the PDF or render line items in your own UI.
payment.succeededwebhookFires once the renewal charge clears. Carries paymentLinkId,
subscriptionId (when the source is a renewal), and the same
metadata you set on creation.
payment.failedwebhookFires when a renewal charge is declined. Surface a retry prompt to the customer or fall back to dunning.
Plan changes
Plan changes are dashboard-driven today. The merchant moves a customer
between products in the Merida UI, which mints a proration invoice and emits
subscription.updated. There's no public REST endpoint for self-service
plan changes yet. Surface upgrades in your own product UI by linking the
customer to a new checkout for the higher-tier product.
Cancellation and pausing
The end-customer manages their own subscription via the
self-service portal. You hand them a tokenised URL
(/portal/<token>). They click cancel, pause, or resume, and Merida fires
subscription.canceled (cancel) or subscription.updated (pause/resume)
so your backend stays in sync.
Two cancellation modes:
period_end(default): keeps the subscription active until the end of the paid period, then doesn't renew. The customer keeps access.immediate: terminates now and refunds nothing automatically. Use when a customer churns mid-cycle and you handle credit on your side.
Pause keeps the subscription billable but skips the next renewal until
resumes_at. Resume undoes the pause. Pausing is a soft alternative to
cancellation: the metadata, plan, and external IDs all carry through.
See the portal endpoints for the exact REST shape.
Correlating to your user
Every subscription event includes externalCustomerId and the full
metadata map you set at checkout creation. Prefer those over the raw
subscriptionId when wiring up entitlement gates. They survive even if the
customer churns and re-subscribes with a fresh subscription record.
Next up
Customer portal →