Skip to content

Checkout

Checkout is Byl's hosted payment page for handling a complete purchase — products, quantities, and discounts included.

When a customer clicks "Buy" in your app or on your website, you create a checkout and redirect them to the url returned in the response. From there, Byl handles everything: choosing a payment method, applying promotion codes, collecting a delivery address and phone number, and emailing the receipt.

As soon as the checkout is paid, a checkout.completed event is delivered via webhook.

Invoice vs Checkout

If you only need to collect a specific amount, an invoice is simpler. Use a checkout when you need products, quantities, promotion codes, or delivery addresses.

How it works

Customer clicks "Buy"


Your server calls POST /checkouts
  │ the response contains id and url

Redirect the customer to the url
  │ they pay on Byl's hosted page

Customer returns to success_url  +  you receive a checkout.completed webhook

We recommend fulfilling orders (shipping goods, granting access) from the webhook rather than the success_url — the customer may close the page before returning.

Statuses

StatusDescription
openAwaiting payment.
completePaid. The checkout.completed webhook has been sent.
expiredExpired. Can no longer be paid.

By default, a checkout expires after 2 months (expires_at). Plan-change checkouts are the exception — they expire after 24 hours (see why).

The Checkout object

FieldTypeDescription
idNumberCheckout ID.
urlStringThe payment page to show the customer.
statusStringStatus: open, complete, expired.
modeStringPurchase mode: payment, subscription.
amount_subtotalNumberTotal of all items (before discounts).
amount_totalNumberTotal the customer pays (after discounts).
client_reference_idStringThe order reference in your system.
customer_idNumberID of the associated customer.
customer_emailStringThe customer's email address.
is_guestBooleanWhether this is a guest checkout (no customer attached).
allow_promotion_codesBooleanWhether the promotion code field is enabled.
expires_atDateWhen the checkout expires.
created_atDateWhen the checkout was created.
updated_atDateWhen the checkout was last updated.

Create a checkout

POST /v1/projects/:project_id/checkouts
ParameterTypeRequiredDescription
items[]ArraytrueList of items. At least 1.
success_urlStringfalseWhere to send the customer after a successful payment.
cancel_urlStringfalseWhere to send the customer if they cancel the purchase.
customer_idNumberfalseCustomer ID. Required for checkouts with a recurring price.
customer_emailStringfalseThe customer's email address. If provided, a customer is created and attached automatically.
client_reference_idStringfalseA reference from your system. Up to 48 characters.
phone_number_collectionBooleanfalseEnable phone number collection.
delivery_address_collectionBooleanfalseEnable delivery address collection.
allow_promotion_codesBooleanfalseEnable the promotion code field.
discounts[]ArrayfalseDirect discounts.
subscription_idNumberfalseID of the subscription to renew or change plans on.

The response contains only the id and url:

json
{
  "data": {
    "id": 13338,
    "url": "https://byl.mn/h/checkout/13338/Yi7smBuk"
  }
}

Specifying items

There are three ways to specify a product in items[]. Use exactly one per item.

MethodWhen to use
priceRecommended. Reference a price registered in Byl by a memorable name (lookup key).
price_idReference a price registered in Byl by its ID. Same as price, but requires remembering the ID.
price_dataPass the price and name of a product not registered in Byl inline in the request (dynamic pricing, shopping carts, etc.).

WARNING

To use product-level promotion codes or subscriptions, the product must be registered in Byl — use price or price_id. These features do not work with price_data.

price (lookup key)

A lookup key is a name you give a price that is unique within your project (e.g. starter_monthly). Set it in the price section of the product edit page in the dashboard.

ParameterTypeRequiredDescription
items[0][price]StringtrueThe price's lookup key. E.g. starter_monthly.
items[0][quantity]NumbertrueQuantity. At least 1.
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 '{
          "success_url": "https://example.mn/purchase/success",
          "items": [
              { "price": "starter_monthly", "quantity": 1 }
          ]
      }'

Passing a lookup key that does not exist in the project returns a validation error.

price_id

The ID of a price registered in Byl. Each price's ID is shown on the product detail page in the dashboard.

ParameterTypeRequiredDescription
items[0][price_id]NumbertrueThe ID of a price registered in Byl.
items[0][quantity]NumbertrueQuantity.
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 '{
          "success_url": "https://example.mn/purchase/success",
          "items": [
              { "price_id": 3, "quantity": 1 }
          ]
      }'

price_data

Define a product not registered in Byl inline in the request. Useful for dynamic pricing (e.g. shopping cart contents, metered services).

ParameterTypeRequiredDescription
items[0][price_data][unit_amount]NumbertrueUnit price.
items[0][price_data][product_data][name]StringtrueProduct name. Up to 255 characters.
items[0][price_data][product_data][client_reference_id]StringfalseThe product's ID in your system.
items[0][quantity]NumbertrueQuantity.
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 '{
          "success_url": "https://example.mn/purchase/success",
          "items": [
              {
                  "price_data": {
                      "unit_amount": 1000,
                      "product_data": { "name": "Product 1" }
                  },
                  "quantity": 1
              }
          ]
      }'

Letting customers adjust quantities

An extra setting that works the same with all three methods above. When enabled, the customer can change the quantity themselves on the checkout page.

ParameterTypeRequiredDescription
items[0][adjustable_quantity][enabled]BooleantrueEnable quantity adjustment.
items[0][adjustable_quantity][min]NumberfalseThe minimum quantity allowed.
items[0][adjustable_quantity][max]NumberfalseThe maximum quantity allowed.
json
{
  "items": [
    {
      "price": "tshirt",
      "quantity": 1,
      "adjustable_quantity": { "enabled": true, "min": 1, "max": 10 }
    }
  ]
}

Discounts

Use discounts[] to subtract a fixed amount directly from the total. You decide the reason for the discount and write a description the customer will see.

ParameterTypeRequiredDescription
discounts[0][amount]NumbertrueThe discount amount.
discounts[0][description]StringtrueDescription shown to the customer. Up to 255 characters.

The total discount amount cannot exceed the items subtotal.

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 '{
          "items": [
              { "price_id": 3, "quantity": 1 }
          ],
          "discounts": [
              { "amount": 5400, "description": "Discount" }
          ]
      }'

In this example, a discount of ₮5,400 appears with the description "Discount":

Byl discounted checkout

Discounts vs Promotion codes

discounts[] directly subtracts a discount you calculated. A promotion code lets the customer enter a code themselves to receive a discount. You can use both at the same time.

Promotion codes

Pass allow_promotion_codes: true to show a promotion code field on the checkout page. When the customer enters a code, the discount is applied automatically.

Promotion codes are created in the dashboard. There are two types, each with different requirements for the checkout:

Code typeHow items must be specified
Discount off the order totalAny method — price, price_id, and price_data all work.
Product discountEvery item must use price or price_id.

Product discount codes are tied to specific products registered in Byl. Products passed via price_data are not registered in the system, so it cannot determine which discount applies to them.

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 '{
          "success_url": "https://example.mn/purchase/success",
          "allow_promotion_codes": true,
          "items": [
              { "price_id": 3, "quantity": 1 },
              { "price_id": 5, "quantity": 2 }
          ]
      }'

Read more on the Promotion codes page.

Recurring payments (subscriptions)

When a checkout with a recurring price (monthly or yearly cycle) is paid, a subscription is created automatically. In this case, 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.

Read about renewals, plan changes, and trials on the Subscriptions page.

Retrieve a checkout

GET /v1/projects/:project_id/checkouts/:checkout_id

TIP

There is no need to poll this endpoint to find out whether a checkout has been paid — set up a webhook and the checkout.completed event will be delivered to you immediately.

Example request

shell
curl -X GET https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/checkouts/13338 \
    -H "Authorization: Bearer $BYL_TOKEN" \
    -H 'Accept: application/json'

Example response

json
{
  "data": {
    "id": 13338,
    "url": "https://byl.mn/h/checkout/13338/Yi7smBuk",
    "client_reference_id": null,
    "mode": "payment",
    "status": "open",
    "expires_at": "2026-12-25T16:00:00.000000Z",
    "amount_subtotal": 1000,
    "amount_total": 1000,
    "customer_id": null,
    "customer_email": null,
    "is_guest": true,
    "allow_promotion_codes": false,
    "created_at": "2026-10-25T10:27:49.000000Z",
    "updated_at": "2026-10-25T10:27:49.000000Z"
  }
}

Next steps