Billing objects
Reference for the products, prices, customers and invoices behind Moosyl subscriptions: fields, endpoints and SDK methods.
Subscriptions are built from four objects. This page lists their fields and endpoints. For how they fit together over a billing cycle, read Subscriptions.
| Object | What it is | Scope |
|---|---|---|
| Product | Something you sell on a schedule, like "Pro plan" | Environment of the key that created it |
| Price | An amount and interval for a product | Organization (through its product) |
| Customer | One of your users, identified by your own user ID | Organization (shared by Sandbox and Production) |
| Invoice | A bill for one billing period | Organization |
All endpoints on this page need your secret key in the Authorization header, without Bearer. Base URL: https://api.moosyl.com. The full list of parameters and responses is in the API reference.
Lists and pagination
List endpoints accept page (default 1) and limit (default 20, max 100) and return:
{
"data": [],
"pagination": { "page": 1, "limit": 20, "total": 0, "totalPages": 0 }
}Single-object endpoints return { "data": { ... } }. The SDK unwraps data for you.
Products
A product groups the prices you charge for one offer.
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Product ID |
name | string | Shown on your side; required |
description | string or null | Optional |
active | boolean | false once archived |
organizationId | string (UUID) | Your organization |
environmentId | string (UUID) or null | The environment it was created in |
createdAt | string (date-time) | Creation time |
prices | Price[] | Active prices, on list and get only |
A product is created in the environment of the key you use. Lists and lookups return the active products of that environment.
import { Moosyl } from "moosyl-sdk";
const moosyl = new Moosyl(process.env.MOOSYL_SECRET_KEY!);
const product = await moosyl.createProduct({ name: "Pro plan", description: "Everything in Pro" });
const { data: products } = await moosyl.listProducts();
const withPrices = await moosyl.getProduct(product.id);curl -X POST https://api.moosyl.com/products \
-H "Authorization: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Pro plan", "description": "Everything in Pro"}'| Action | Endpoint | SDK |
|---|---|---|
List (filter by id) | GET /products | listProducts({ id?, page?, limit? }) |
| Get, with prices | GET /products/:id | getProduct(id) |
| Create | POST /products | createProduct({ name, description? }) |
| Update | PATCH /products/:id | updateProduct(id, { name?, description? }) |
| Archive | PATCH /products/:id/archive | archiveProduct(id) |
Archiving sets active to false. The product then disappears from lists and lookups and can't be updated or restored. Its prices and existing subscriptions are not changed.
Prices
A price is what a subscription bills: a whole amount in MRU every week, month or year.
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Price ID |
productId | string (UUID) | The product it belongs to |
amount | integer | Amount in whole MRU, billed each interval |
interval | weekly | monthly | yearly | How often it bills |
active | boolean | false once archived or replaced |
createdAt | string (date-time) | Creation time |
The SDK has no price methods yet, so use HTTP:
curl -X POST https://api.moosyl.com/prices \
-H "Authorization: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"productId": "PRODUCT_ID", "amount": 1000, "interval": "monthly"}'| Action | Endpoint | Body |
|---|---|---|
| Get | GET /prices/:id | |
| Create | POST /prices | productId, amount, interval |
| Update | PATCH /prices/:id | amount?, interval?, replaceExisting? |
| Archive | PATCH /prices/:id/archive |
To list a product's prices, get the product: it includes its active prices.
Changing a price
A subscription keeps the price ID it was created with, and each renewal bills that price's current amount. Updating a price in place changes what its existing subscriptions pay from their next invoice. To leave existing subscriptions on the old amount, send replaceExisting: true: Moosyl archives the old price and returns a new price (new id) for new subscriptions.
Archiving a price hides it from lookups and from its product's price list. Subscriptions already on it keep billing.
Customers
A customer links your user to their subscriptions and invoices. Identify them with your own user ID in externalUserId.
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Customer ID |
externalUserId | string | Your user ID; unique within your organization |
phone | string or null | Optional, 8-digit local number; used for their payment requests |
organizationId | string (UUID) | Your organization |
createdAt | string (date-time) | Creation time |
Customers belong to your organization, so Sandbox and Production keys see the same customers.
const customer = await moosyl.createCustomer({ externalUserId: "user_abc123", phone: "22222222" });
const { data } = await moosyl.listCustomers({ externalUserId: "user_abc123" });curl -X POST https://api.moosyl.com/customers \
-H "Authorization: YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"externalUserId": "user_abc123", "phone": "22222222"}'| Action | Endpoint | SDK |
|---|---|---|
List (filter by id or externalUserId) | GET /customers | listCustomers({ id?, externalUserId?, page?, limit? }) |
| Create | POST /customers | createCustomer({ externalUserId, phone? }) |
| Update | PATCH /customers/:id | updateCustomer(id, { externalUserId?, phone? }) |
There's no get or delete endpoint: look a customer up with GET /customers?id=… or ?externalUserId=…. Creating a second customer with the same externalUserId fails. To create the customer and the subscription in one call, use createSubscriptionByExternalUser (see Subscriptions).
Invoices
Moosyl creates an invoice for each billing period of a subscription, with a payment request your customer pays. You can't create invoices through the API.
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Invoice ID; also the transactionId of its payment request |
customerId | string (UUID) | The customer billed |
status | pending | paid | void | refunded | See below |
amount | string | Decimal amount in MRU, for example "1000.00" |
dueDate | string (date-time) or null | Due date: the subscription's next billing date when the invoice was created |
paymentRequestId | string (UUID) or null | The payment request to pay it |
organizationId | string (UUID) | Your organization |
createdAt | string (date-time) | Creation time |
| Status | Meaning |
|---|---|
pending | Waiting for payment |
paid | The payment request was paid |
void | The subscription was cancelled while the invoice was unpaid |
refunded | Refunded. Not set automatically by the API today |
const { data: invoices } = await moosyl.listInvoices({ subscriptionId: subscription.id });curl "https://api.moosyl.com/invoices?subscriptionId=SUBSCRIPTION_ID" \
-H "Authorization: YOUR_SECRET_KEY"| Action | Endpoint | SDK |
|---|---|---|
List (filter by id, externalUserId or subscriptionId) | GET /invoices | listInvoices({ id?, externalUserId?, subscriptionId?, page?, limit? }) |
To collect an unpaid invoice, send your customer to its payment request, for example with a checkout session created from paymentRequestId.
Next steps
- Subscriptions: create subscriptions and follow the billing cycle
- Webhooks:
subscription-updatedandpayment-request-createdevents - API reference: every parameter and response
Subscriptions
Bill customers on a schedule with Moosyl: create products, prices, customers and subscriptions, and get an invoice and a payment request every billing cycle.
Platform Connect
Let merchants connect their Moosyl account to your platform in a few clicks. You receive their API keys and a webhook, so you can take payments on their behalf.