Skip to content

Subscriptions

Subscriptions let you run services billed on a monthly or yearly cycle (SaaS plans, memberships, content access, etc.) entirely on Byl.

Mongolian payment systems have no automatic charging (auto-charge), so Byl subscriptions work on a request-to-pay basis: at the end of each billing period, the customer receives a reminder and renews their access by paying themselves. Byl handles reminders, expiry tracking, status transitions, and webhook notifications automatically.

Customers manage their subscriptions themselves in the billing portal — a page accessed via a temporary session link, with no Byl account or password required, where they can renew, change plans, cancel, and view their payment history.

How it works

  1. In the dashboard, create a recurring price on your product (with a monthly or yearly cycle).
  2. When your customer wants to subscribe, you create a checkout with the recurring price.
  3. As soon as the checkout is paid, a subscription is created automatically and a subscription.created webhook is sent.
  4. Before the billing period ends, Byl emails the customer a reminder (with a portal link) and sends you a subscription.renewal_due webhook.
  5. If the customer pays in the portal, the period is renewed (subscription.renewed); if they don't, the subscription is canceled once the period ends (subscription.canceled).

Statuses

StatusDescription
trialingIn trial. Becomes active if the customer pays when the trial ends.
activeActive. Access remains open until the end of the billing period.
past_dueOverdue (only on products with a grace period configured).
canceledCanceled. The status at which the customer's access should be revoked.

When to revoke access

Keep the customer's access open until the subscription.canceled event arrives — the trialing, active, and past_due statuses all count as entitled.

Reminder schedule

Depending on the billing cycle, reminder emails are sent to the customer on the following schedule, and you receive a subscription.renewal_due webhook each time:

  • Monthly plans — 7 days and 1 day before expiry
  • Yearly plans — 30 days, 7 days, and 1 day before expiry

Customers who have requested cancellation do not receive reminders.

Starting a subscription

A subscription is created automatically when a checkout with a recurring price is paid. Compared to a regular checkout, two extra requirements apply:

  • customer_id is required — the subscription is registered to a specific customer
  • A single item — a checkout with a recurring price cannot contain any other items

Get the customer_id from the Create customer endpoint — when you pass a client_reference_id it behaves as an upsert, so calling it before every checkout will not create duplicates.

Example request

shell
BYL_PROJECT_ID="your project ID"
BYL_TOKEN="your API token"

curl -X POST https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/checkouts \
    -H "Authorization: Bearer $BYL_TOKEN" \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -d '{
          "customer_id": 12,
          "success_url": "https://example.mn/subscribe/success",
          "items": [
              { "price": "starter_monthly", "quantity": 1 }
          ]
      }'

As soon as the checkout is paid, the subscription is created and a subscription.created webhook is sent. The customer receives an email with a portal link.

One product — one subscription

A customer can have only one active subscription per product.

  • A checkout for the same price on the same product by a customer with an active subscription does not create a new subscription — it renews the existing one.
  • To create a checkout with a different price, you must pass subscription_id — otherwise the request returns an error (this prevents the credit for remaining days from being lost).

Plan limits

The total number of active subscriptions depends on your Byl plan — 10 on the Starter plan, unlimited on Growth. Once the limit is reached, requests to start a new subscription return an error; renewals and plan changes on existing subscriptions are not affected. The limit is not enforced in test mode.

Renewals

Customers renew by clicking "Renew" and paying in the portal, so in most cases no extra work is required on your side. If you want to create a renewal checkout from your own system, pass the subscription_id parameter:

shell
curl -X POST https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/checkouts \
    -H "Authorization: Bearer $BYL_TOKEN" \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -d '{
          "customer_id": 12,
          "subscription_id": 4,
          "items": [
              { "price": "starter_monthly", "quantity": 3 }
          ]
      }'
  • The renewal is added on top of the current expiry date — no remaining days are lost.
  • quantity specifies how many billing periods to renew for (3 months in this example).

Changing plans

Passing a different recurring price on a checkout with a subscription_id performs a plan change — the subscription moves to the new plan and a new billing period starts on the day of payment. You can even switch to a price on a different product (e.g. Starter → Growth).

The value of the remaining days in the current period becomes a credit and is automatically deducted from the new checkout's total. If the credit is greater than or equal to the new plan's price (e.g. downgrading from a yearly to a monthly plan), a mid-period change is not possible — the change happens when the period ends.

WARNING

The credit is calculated at the moment the checkout is created, so a plan-change checkout must be paid within 24 hours. If it expires, create a new checkout.

The portal's "Change plan" section automatically shows the project's other recurring prices, so you don't need to build this flow yourself.

Trials

A trial subscription starts without payment, so it is created directly via the API rather than through a checkout. This is the only way to create a subscription via the API — paid subscriptions always start from a checkout.

POST /v1/projects/:project_id/subscriptions
ParameterTypeRequiredDescription
customer_idNumbertrueCustomer ID.
priceStringtrue*The recurring price's lookup_key.
price_idNumbertrue*The recurring price's ID — may be passed instead of price.
trial_daysNumbertrueTrial length in days (1–365).

* You must pass either price or price_id. If both are given, price_id takes precedence. The price must be recurring.

shell
curl -X POST https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/subscriptions \
    -H "Authorization: Bearer $BYL_TOKEN" \
    -H 'Accept: application/json' \
    -H 'Content-Type: application/json' \
    -d '{
          "customer_id": 12,
          "price": "starter_monthly",
          "trial_days": 14
      }'

Before the trial ends, the customer receives the usual reminders; the subscription becomes active when they pay in the portal, and is canceled if they don't.

WARNING

A customer can take a trial on a given product only once. If they have already had a trial, or already have access to that product, the request returns an error.

Cancellation

Cancellation takes effect at the end of the billing period:

  • At the moment of cancellation, the status remains active and a subscription.updated webhook is sent (with the canceled_at field populated).
  • The customer's access remains valid until the end of the period, but no further reminders are sent.
  • Once the period ends, the status becomes canceled and a subscription.canceled webhook is sent — this is when you revoke access.
  • The cancellation can be reversed (resumed) before the period ends.

Customers can cancel themselves in the portal, and you can also cancel via the API:

POST /v1/projects/:project_id/subscriptions/:subscription_id/cancel
POST /v1/projects/:project_id/subscriptions/:subscription_id/resume
shell
# Cancel
curl -X POST https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/subscriptions/4/cancel \
    -H "Authorization: Bearer $BYL_TOKEN" \
    -H 'Accept: application/json'

# Resume (undo cancellation)
curl -X POST https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/subscriptions/4/resume \
    -H "Authorization: Bearer $BYL_TOKEN" \
    -H 'Accept: application/json'

These requests return 409 Conflict on a subscription that has already been fully canceled.

Portal

Customers manage their subscriptions themselves in the billing portal: renew, change plans, cancel/resume, resubscribe, and view their payment history. Whenever a subscription is created, a reminder is due, or a subscription is canceled, Byl automatically emails the customer a branded message with a portal link, so in most cases no extra work is required on your side.

To give customers access from inside your own application (a "My subscription" menu, for example), create a temporary session link with POST /billing-portal/sessions and redirect them to it.

The Subscription object

FieldTypeDescription
idNumberSubscription ID.
statusStringStatus: trialing, active, past_due, canceled.
customer_idNumberCustomer ID.
product_idNumberProduct ID.
price_idNumberPrice ID (changes on plan change).
current_period_startDateStart of the current billing period.
current_period_endDateEnd of the current billing period (when access expires).
trial_ends_atDateWhen the trial ends (null if there is no trial).
canceled_atDateWhen cancellation was requested (null if not canceled).
is_testBooleanWhether the subscription was created in test mode.
customerObjectCustomer details (included in retrieve requests).
priceObjectPrice and product details (included in retrieve requests).

List subscriptions

GET /v1/projects/:project_id/subscriptions

Returns 25 records per page; fetch the next page with ?page=2. Filter with the following parameters:

ParameterDescription
customer_idSubscriptions belonging to a specific customer.
price_idSubscriptions on a specific price.
priceFilter by a price's lookup_key.
statustrialing, active, past_due, canceled.
shell
curl -X GET "https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/subscriptions?customer_id=12&status=active" \
    -H "Authorization: Bearer $BYL_TOKEN" \
    -H 'Accept: application/json'

Retrieve a subscription

GET /v1/projects/:project_id/subscriptions/:subscription_id

Example response

json
{
  "data": {
    "id": 4,
    "status": "active",
    "project_id": 1,
    "customer_id": 12,
    "product_id": 7,
    "price_id": 3,
    "current_period_start": "2026-07-23T04:10:00.000000Z",
    "current_period_end": "2026-08-23T15:59:59.000000Z",
    "trial_ends_at": null,
    "canceled_at": null,
    "is_test": false,
    "customer": {
      "id": 12,
      "name": "Бат-Эрдэнэ",
      "email": "[email protected]",
      "phone": "99999999",
      "client_reference_id": "user_842"
    },
    "price": {
      "id": 3,
      "type": "recurring",
      "unit_amount": 30000,
      "recurring_interval": "month",
      "recurring_interval_count": 1,
      "lookup_key": "starter_monthly",
      "product": {
        "id": 7,
        "name": "Starter багц",
        "client_reference_id": null
      }
    },
    "created_at": "2026-06-23T04:10:00.000000Z",
    "updated_at": "2026-07-23T04:10:00.000000Z"
  }
}

Checking whether a customer has access

The simplest way to answer "is this user subscribed?" — retrieving a customer includes their entitled subscriptions:

GET /v1/projects/:project_id/customers/:customer_id
GET /v1/projects/:project_id/customers/by-client-reference-id/:client_reference_id

The data.subscriptions array contains only entitled subscriptions (trialing, active, past_due).

Webhook events

EventWhen it is sent
subscription.createdA new subscription was created (a checkout was paid or a trial started).
subscription.renewedThe billing period was successfully renewed.
subscription.updatedThe plan was changed, cancellation was requested, or a cancellation was reversed.
subscription.renewal_dueA renewal reminder was sent.
subscription.past_dueThe subscription is overdue (only on products with a grace period configured).
subscription.canceledThe subscription was fully canceled — revoke the customer's access at this point.

Each event's data.object contains the subscription object (with customer, product, and price details). Read about webhook configuration and signature verification on the Webhook page.

Next steps

  • Billing portal — the self-service page where customers manage their subscriptions
  • Checkout — create a checkout to start a subscription
  • Webhook — receive subscription events in your system