Bank transfers — Confirming a transfer
A payment made by bank transfer is confirmed or rejected once someone has checked the bank statement. Besides the dashboard, that verdict can come over the API. It matters most for platform teams: in a client's project the money lands in the client's account, so you show the payment.awaiting_verification event to your client in your own system and send their answer back here.
Both project tokens and team tokens work; the token needs write (a read token gets 403). The verdict is recorded in the name of the token's user.
Flow
Customer clicks "I've transferred"
│ payment.awaiting_verification webhook (data.object = the payment)
▼
Your system shows the reference and amount to your client
│ the client checks the statement
▼
POST .../bank-transfer/confirm or .../bank-transfer/reject
│
▼
checkout.completed / invoice.paid (when confirmed)In a client's project Byl also emails the client's contact address a time-limited link that needs no login, where the client can answer "Received" / "Not received". The API is for teams that want this in their own UI; whichever path decides first wins, the next gets 409.
The Payment object
The response has the same shape as the data.object of the payment.awaiting_verification webhook: id, status (pending, paid, failed), amount, reference, bank_name, account_number, claimed_at, expires_at, payable (type, id, url, status, plus number for invoices), customer_email, phone_number, is_test, created_at.
Confirm
POST /v1/projects/:project_id/payments/:payment_id/bank-transfer/confirm| Parameter | Type | Required | Description |
|---|---|---|---|
received_amount | Number | true | The amount that actually arrived. |
acknowledge_mismatch | Boolean | false | true to count the payment as paid although the amounts differ. |
Once confirmed the payment is paid and the regular checkout.completed / invoice.paid webhook, notifications and receipt follow.
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/payments/9d2f6c1e-1b1c-4a2e-9c7d-2d1f4a8b9e10/bank-transfer/confirm \
-H "Authorization: Bearer $BYL_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{ "received_amount": 45000 }'Example response
{
"data": {
"id": "9d2f6c1e-1b1c-4a2e-9c7d-2d1f4a8b9e10",
"project_id": 1,
"status": "paid",
"driver": "bank_transfer",
"amount": 45000,
"description": "Checkout - 231",
"reference": "482913",
"bank_name": "Khan Bank",
"account_number": "5012345678",
"claimed_at": "2026-08-27T09:30:00.000000Z",
"expires_at": "2026-08-30T09:30:00.000000Z",
"payable": {
"type": "checkout",
"id": 231,
"url": "https://byl.mn/h/checkout/231/…",
"status": "complete"
},
"customer_email": "[email protected]",
"phone_number": "99112233",
"is_test": false,
"created_at": "2026-08-27T09:20:00.000000Z"
}
}Reject
POST /v1/projects/:project_id/payments/:payment_id/bank-transfer/reject| Parameter | Type | Required | Description |
|---|---|---|---|
reason | String | true | Up to 255 characters. Shown to the customer. |
The payment becomes failed and the checkout reopens, so the customer can transfer again with a new reference or pay another way. When we know the customer's email, they are emailed the reason.
Errors
| Status | error | Meaning |
|---|---|---|
409 | already_settled | The transfer was already confirmed, rejected, or has expired. |
422 | not_bank_transfer | The payment is not a bank transfer. |
422 | — | Validation: a differing amount without acknowledge_mismatch gives errors.acknowledge_mismatch. |
403 | — | A read token, or a token of another team or project. |
404 | — | The payment is not in this project. |