Billing API
Billing API is the current API for merchant checkout and billing. All endpoints use bearer API-key authentication.
Authorization: Bearer st_live_or_dev_key
Content-Type: application/json
For create requests, send an Idempotency-Key header. SDKs also accept an idempotency key in the request object and generate one when needed.
Idempotency-Key: checkout-order-1001
Servers
| Environment | Base URL |
|---|---|
| Mainnet | https://api.stendly.com |
| Devnet | https://api-devnet.stendly.com |
Billing modes
| Mode | What it means | Typical use |
|---|---|---|
one_time | A single hosted payment request. Can use a catalog price or a custom amountCents. | Online order, license purchase, setup fee. |
balance_top_up | Prepaid credit purchase that credits the customer balance ledger after payment. | AI credits, account balance, metered usage prep. |
subscription | Managed renewal billing. Stendly creates renewal invoices; customers confirm each renewal payment unless you build a balance-based flow. | SaaS plans, memberships, renewal billing. |
Recommended flow
- Create a project.
- For the simplest no-code or low-code sale, create a Payment Link from the dashboard or
POST /api/payment-links/setup. - For a custom one-time payment, create a checkout session with
amountCents. - For reusable catalog items, balance top-ups, or managed subscriptions, create a product and price.
- Redirect the customer to a Payment Link URL or to the returned checkout session
checkoutUrl. - Fulfill access after webhook confirmation.
Projects
Billing overview
GET /api/billing/overview
Returns projects, API keys, roles, products, prices, customers, checkouts, invoices, subscriptions, portal sessions, webhook deliveries, and usage summaries for the current merchant.
| Operation | Method | Path |
|---|---|---|
| List projects | GET | /api/projects |
| Retrieve project | GET | /api/projects/{projectId} |
| Create project | POST | /api/projects |
| Update project | PATCH | /api/projects/{projectId} |
| Delete project | DELETE | /api/projects/{projectId} |
Create project
{
"name": "Production",
"environment": "live",
"payoutAddress": "E7g2wdh9Z7a5vZkpQmdRZaVJ5z9pK2P38a6GKxeJ2Hc8",
"monthlyVolumeLimitCents": 100000000,
"monthlyRequestLimit": 100000
}
monthlyVolumeLimitCents is stored in cents. For example, 100000000 means $1,000,000.00.
Customers
Customers are optional for one-time checkout. They are useful for subscriptions, invoices, usage, and customer portal sessions. Checkout can create a customer automatically from externalCustomerId, customerEmail, or customerName.
{
"projectId": "project_uuid",
"externalCustomerId": "telegram_123456",
"email": "customer@example.com",
"name": "Customer Name"
}
Catalog
Products define what you sell. Prices define how you charge. Catalog is optional for custom one-time payment requests, but required when Stendly needs product semantics such as balance credits or managed subscription renewals.
One-time price
{
"projectId": "project_uuid",
"productId": "product_uuid",
"name": "Online order",
"unitAmountCents": 4999,
"currency": "USD",
"billingMode": "one_time"
}
Balance top-up price
{
"projectId": "project_uuid",
"productId": "product_uuid",
"name": "10,000 AI credits",
"unitAmountCents": 1000,
"currency": "USD",
"billingMode": "balance_top_up",
"creditAmount": 10000
}
Subscription price
{
"projectId": "project_uuid",
"productId": "product_uuid",
"name": "Pro semiannual",
"unitAmountCents": 24900,
"currency": "USD",
"billingMode": "subscription",
"recurringInterval": "month",
"recurringIntervalCount": 6
}
recurringInterval must be day, week, month, or year. recurringIntervalCount defaults to 1; use 6 with month for semiannual billing.
Payment Links
Payment Links are reusable public checkout entry points for a fixed project and price. Opening a Payment Link does not create a checkout session, invoice, payment intent, customer, API key, or webhook. A checkout session is created only when the buyer confirms checkout.
| Operation | Method | Path |
|---|---|---|
| List payment links | GET | /api/payment-links |
| Retrieve payment link | GET | /api/payment-links/{paymentLinkId} |
| Create payment link | POST | /api/payment-links |
| One-step setup | POST | /api/payment-links/setup |
| Update payment link | PATCH | /api/payment-links/{paymentLinkId} |
| Delete inactive payment link | DELETE | /api/payment-links/{paymentLinkId} |
One-step setup
Use setup for onboarding. It creates a product, price, and reusable Payment Link atomically. It does not create a checkout session, invoice, payment intent, customer, API key, or webhook.
{
"projectId": "project_uuid",
"productName": "VPN Pro",
"billingMode": "subscription",
"unitAmountCents": 1000,
"currency": "USD",
"recurringInterval": "month",
"requireClientReferenceId": true,
"collectCustomerEmail": true
}
The response includes paymentLink.url, for example:
https://app.stendly.com/p/pl_example
You can append a buyer reference when linking from your app:
https://app.stendly.com/p/pl_example?client_reference_id=user_8472
client_reference_id is returned on checkout/webhook records as a reconciliation label. It is controlled by the browser URL and must not be treated as identity proof. Use backend-created checkout sessions with trusted externalCustomerId or merchantReference when entitlement security depends on verified server-side identity.
Public Payment Link endpoints
These endpoints do not require an API key.
| Operation | Method | Path |
|---|---|---|
| Read public link | GET | /api/payment-links/{publicToken} |
| Start checkout | POST | /api/payment-links/{publicToken}/checkout |
GET /api/payment-links/{publicToken} is read-only. POST /checkout accepts only clientReferenceId, customerEmail, customerName, and idempotencyKey; it cannot change project, price, amount, billing mode, payout, redirects, or scopes.
Checkout sessions
| Operation | Method | Path |
|---|---|---|
| List checkout sessions | GET | /api/checkout-sessions |
| Retrieve checkout session | GET | /api/checkout-sessions/{sessionId} |
| Create checkout session | POST | /api/checkout-sessions |
Custom one-time checkout
Use this when your application already owns the cart, order, or entitlement model.
{
"projectId": "project_uuid",
"amountCents": 4999,
"mode": "one_time",
"merchantReference": "order_1001",
"successUrl": "https://example.com/success",
"cancelUrl": "https://example.com/cancel"
}
Catalog checkout
{
"projectId": "project_uuid",
"priceId": "price_uuid",
"mode": "one_time",
"merchantReference": "order_1001",
"successUrl": "https://example.com/success",
"cancelUrl": "https://example.com/cancel"
}
Balance top-up checkout
{
"projectId": "project_uuid",
"priceId": "price_uuid",
"mode": "balance_top_up",
"merchantReference": "topup_1001",
"successUrl": "https://example.com/success",
"cancelUrl": "https://example.com/cancel"
}
Subscription checkout
{
"projectId": "project_uuid",
"priceId": "price_uuid",
"mode": "subscription",
"externalCustomerId": "telegram_123456",
"customerEmail": "customer@example.com",
"customerName": "Website Customer",
"merchantReference": "sub_1001",
"successUrl": "https://example.com/success",
"cancelUrl": "https://example.com/cancel"
}
Response includes checkoutUrl. Redirect the customer to that hosted URL.
Invoices
Invoices are internal billable records created by checkout and subscription renewal flows. Most integrations create checkout sessions, then read invoice status and webhook events for reconciliation.
Subscriptions
| Operation | Method | Path |
|---|---|---|
| List subscriptions | GET | /api/subscriptions |
| Retrieve subscription | GET | /api/subscriptions/{subscriptionId} |
| Update subscription | PATCH | /api/subscriptions/{subscriptionId} |
| Cancel subscription | POST | /api/subscriptions/{subscriptionId}/cancel |
| Preview change | POST | /api/subscriptions/{subscriptionId}/preview-change |
Cancel subscription
{
"atPeriodEnd": true
}
Subscription renewal does not silently debit a non-custodial wallet. Stendly creates the next invoice and checkout session; the customer confirms payment, then webhooks tell your server to keep or update access.
Usage
Usage records are stored for metered billing and reporting. Use idempotency keys that identify one usage event or message.
{
"projectId": "project_uuid",
"metric": "tokens",
"quantity": 1200,
"customerId": "customer_uuid",
"subscriptionId": "subscription_uuid",
"idempotencyKey": "usage-message-123"
}
Customer portal sessions
{
"projectId": "project_uuid",
"customerId": "customer_uuid",
"returnUrl": "https://example.com/account"
}
Webhook deliveries
Use delivery logs to inspect failed webhooks and replay failed deliveries after fixing your endpoint.
Webhook events
Verify every webhook with X-Stendly-Signature.
Common billing event families:
checkout.session.*invoice.*subscription.*customer.balance_*payment_intent.*
Production handlers should branch on event type and object status before fulfilling access.