Codex · III

Using the API

Use it without the buy button

The buy button is a convenience layer. Underneath, Coin Moebius is a rented webhook plus a few JSON endpoints. If you'd rather build your own UI, run your own checkout flow, or plug Coin Moebius into something the button can't handle (Square or Authorize.Net subscriptions, a custom payment form, a server-side script, a mobile app), every endpoint the button uses is also callable from anything. No code changes on our side.

Authenticating backend calls

Calls you make from your own server can carry your project's API key as a bearer token. Create a key in the dashboard, under your project's API keys (it starts with cmk_), and send it in the Authorization header:

Authorization: Bearer cmk_live_xxxxxxxxxxxxxxxxxxxxxxxx

The key is tied to one project, and authenticated calls get a higher rate limit than anonymous ones. The buy button itself is public and needs no key. Keep your key on your server. Never put it in browser or other client-side code.

These endpoints all live under your project URL. In production that base is https://api.coinmoebius.com. The full URL for each is shown below (replace the {…} parts with your own values).

EndpointWhat it does
POST https://api.coinmoebius.com/api/checkout/{provider}/{projectId}Start a checkout. POST { productId, metadata }; receive back whatever the provider needs (a redirect URL for Stripe, an approval URL for PayPal, a token for Authorize.Net Accept Hosted). Render it however you want.
POST https://api.coinmoebius.com/webhook/{provider}/{projectId}The provider posts here. We verify the signature, normalize the event, store it, count quota. Point the provider's webhook at this URL whether the original checkout came from our button or your own integration.
GET https://api.coinmoebius.com/status/{projectId}/{txId}Poll for the current state of a payment or subscription. Returns the same normalized shape the buy button gets.
POST https://api.coinmoebius.com/api/subscriptions/{projectId}/{subscriptionId}/portal-urlGenerate a provider-hosted portal URL for the buyer to manage their subscription. Works for any provider that has a portal.

Identify the buyer in your own system by passing metadata.customerRef on the checkout call. It threads through the provider and back on every webhook event, so you can join Coin Moebius's records to your own user database without us storing anything about the buyer.

Why someone uses this path: a static-site builder who wants to write their own button to match their site's design and just needs the webhook handled. A developer who wants to skip the button entirely and call the API from their own server. A merchant who wants to run Square or Authorize.Net subscriptions and is fine wiring up card collection themselves. The button is a starting point. The API is the actual product.

Listening for buyer events

The element fires three browser events. Listen with addEventListener on the element (or on document, the events bubble). All events are cancelable, calling event.preventDefault() stops the default flow.

EventWhen it firesDetail payload
cm-load-providersThe picker modal is about to ask the API for the list of providers configured on this project.Empty.
cm-checkout-startedThe buyer picked a provider and Coin Moebius is about to create a checkout session (Stripe / NOWPayments) or generate a reference code (manual).{ provider: 'stripe' | 'nowpayments' | 'manual', ... }
cm-errorSomething failed: network error, signature failure, no provider configured.{ error: Error }
document.addEventListener('cm-error', (event) => {
  console.error('Coin Moebius:', event.detail.error);
  // Show your own error UI, send to your analytics, etc.
});

There is no cm-success event in the buyer's browser. By the time the payment actually completes, the buyer has been redirected to the payment provider's hosted checkout (Stripe Checkout, NOWPayments invoice page). They return to your site via the success_url you configured (see the next section), and your server learns about the payment via the dashboard or by polling the /status endpoint.

Polling from your backend

For a server-side source of truth, poll GET /status/:projectId/:txId from your backend. The response shape:

{
  "status": "succeeded",
  "amount": 29.99,
  "currency": "USD",
  "isTest": false,
  "createdAt": "2026-05-14T01:04:21.000Z",
  "updatedAt": "2026-05-14T01:04:21.000Z"
}

Status values follow the same enum as the dashboard (see Transaction statuses). The endpoint is unauthenticated but rate-limited to 60 requests / minute per IP. The transaction id you pass is whatever the SDK returned in the cm-checkout-started event or what's shown in the dashboard's Reference column.

A typical pattern: when your success_url page loads, kick off a backend job that polls /status/:projectId/:txId every 15 seconds until it sees succeeded (or failed / a timeout), then fulfill the order.

Ready to wire it up?

Free tier covers most sites and never asks for a card.