Invoices
An invoice is the most direct way to request a specific amount from a customer. Creating one returns a unique web address (url) — direct your customer there, and Byl handles everything: choosing a payment method, paying, and receiving a receipt.
As soon as an invoice is paid, an invoice.paid event is sent via webhook, so your system knows without delay.
Invoice vs Checkout
To collect a specific amount, use an invoice. For a purchase involving products, quantities, coupon codes, or shipping addresses, Checkout is a better fit.
Statuses
| Status | Description |
|---|---|
draft | Created but not finalized. Does not accept payment. |
open | Finalized and awaiting payment. Can be shown to the customer. |
paid | Paid. The invoice.paid webhook has been sent. |
void | Voided. Can no longer be paid. |
A newly created invoice moves straight to open because of the auto_advance setting (default true). Pass auto_advance: false to create it in the draft status.
The invoice object
| Field | Type | Description |
|---|---|---|
id | Number | The invoice ID. |
status | String | Status: draft, open, paid, void. |
amount | Number | Amount (in tugrik). |
description | String | Description. |
number | String | Unique invoice number. |
url | String | The invoice web page to show the customer. |
customer_id | Number | ID of the associated customer. |
project_id | Number | The Byl project ID. |
due_date | Date | Payment due date. |
created_at | Date | Creation date. |
updated_at | Date | Last updated date. |
Create an invoice
POST /v1/projects/:project_id/invoices| Parameter | Type | Required | Description |
|---|---|---|---|
amount | Number | true | Amount. Minimum 10, up to 2 decimal places. |
description | String | false | Description. Up to 255 characters. |
due_date | Date | false | Payment due date. Must be a future date. Default: 1 day from now. |
auto_advance | Boolean | false | If true (default), the invoice moves straight to open. If false, it stays in draft. |
customer_id | Number | false | A customer ID. Must belong to the project. |
Example request
BYL_PROJECT_ID="your project ID"
BYL_TOKEN="your API token"
curl -X POST https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/invoices \
-H "Authorization: Bearer $BYL_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"amount": 500,
"description": "Test invoice",
"auto_advance": true
}'Example response
{
"data": {
"id": 5708,
"status": "open",
"amount": 500,
"description": "Test invoice",
"number": "DEMO-0011",
"customer_id": null,
"project_id": 1,
"url": "https://byl.mn/h/invoice/5708/XN3GbRBxTslkMCeDj10CJtqlHiPfcmZ8",
"due_date": "2026-08-06T13:13:07.000000Z",
"created_at": "2026-08-05T13:13:07.000000Z",
"updated_at": "2026-08-05T13:13:07.000000Z"
}
}Direct your customer to the url in the response. The address contains a secret, making it impossible to guess, but we still recommend sharing it only with that customer.
Retrieve an invoice
GET /v1/projects/:project_id/invoices/:invoice_idUse this to check whether an invoice has been paid, or to re-read its amount and number.
TIP
There is no need to poll this endpoint to find out when a payment lands — set up a webhook and the invoice.paid event will reach you immediately.
Example request
curl -X GET https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/invoices/5708 \
-H "Authorization: Bearer $BYL_TOKEN" \
-H 'Accept: application/json'Example response
{
"data": {
"id": 5708,
"status": "paid",
"amount": 500,
"description": "Test invoice",
"number": "DEMO-0011",
"customer_id": null,
"project_id": 1,
"url": "https://byl.mn/h/invoice/5708/XN3GbRBxTslkMCeDj10CJtqlHiPfcmZ8",
"due_date": "2026-08-06T13:13:07.000000Z",
"created_at": "2026-08-05T13:13:07.000000Z",
"updated_at": "2026-08-05T13:13:20.000000Z"
}
}Void an invoice
POST /v1/projects/:project_id/invoices/:invoice_id/voidVoids the invoice — it will no longer accept payment. Voided invoices remain in your history and reports, so we recommend voiding a mistakenly created invoice rather than deleting it.
draft status only
Currently, voiding via the API only works on invoices in the draft status. Calling it on an invoice in the open, paid, or void status returns 403.
Note that because auto_advance defaults to true, invoices created via the API move straight to open — if you may need to void an invoice later, create it with auto_advance: false. An invoice that is already open can be voided from the dashboard.
Example request
curl -X POST https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/invoices/5711/void \
-H "Authorization: Bearer $BYL_TOKEN" \
-H 'Accept: application/json'Example response
{
"data": {
"id": 5711,
"status": "void",
"amount": 500,
"description": "Test invoice",
"number": "DEMO-0011",
"customer_id": null,
"project_id": 1,
"url": "https://byl.mn/h/invoice/5711/XN3GbRBxTslkMCeDj10CJtqlHiPfcmZ8",
"due_date": "2026-09-08T16:33:41.000000Z",
"created_at": "2026-09-07T16:33:41.000000Z",
"updated_at": "2026-09-07T16:33:50.000000Z"
}
}Delete an invoice
DELETE /v1/projects/:project_id/invoices/:invoice_idHides the invoice from your list. The response returns a deleted_at timestamp.
draft status only
Deleting likewise only works on invoices in the draft status — other statuses return 403. Finalized invoices are never deleted, to preserve the integrity of your reports and history.
Example request
curl -X DELETE https://byl.mn/api/v1/projects/$BYL_PROJECT_ID/invoices/5711 \
-H "Authorization: Bearer $BYL_TOKEN" \
-H 'Accept: application/json'Example response
{
"data": {
"id": 5711,
"deleted_at": "2026-09-07T16:30:35.000000Z"
}
}