Platform — Clients and projects
A platform team that opens projects on behalf of its clients uses this API to register a client, show the client's verification link in its own onboarding, open projects, take them live and set their payout account. Everything the Clients menu of the dashboard does is available here.
Platform mode
Platform mode is switched on for your team by the Byl team. Without it these endpoints return 404. Write to [email protected].
Team token
These endpoints take a team token, not a project token: create one under the account-level API tokens menu (visible to the team owner). A team token is also valid on the project endpoints of every project of the team, yours and your clients', so one token is enough to create checkouts and invoices in a client's project. See API token.
Authorization: Bearer <team token>A project token or a legacy token returns 403.
The Client object
| Field | Type | Description |
|---|---|---|
id | Number | The client's (business) ID. The business_id of webhook payloads. |
client_reference_id | String | Your own ID for the client. |
name | String | Business name, copied from TrustAPI once verified; null before. |
type | String | Business type (person, company …), once verified. |
contact_email | String | Where the verification link is sent. |
phone | String | Phone, once verified. |
status | String | Onboarding status, see below. |
is_verified | Boolean | Every step done: the client's projects can go live. |
verified_at | Date | When that happened. |
verification_status | String | Status of the current TrustAPI attempt (pending, submitted, under_review, verified, rejected, expired). |
steps | Array | trustapi, qpay, bank, each with status (pending, in_progress, done, error) and error. |
qpay_merchant_id | String | The QPay merchant created for the client. |
bank_accounts | Array | Active bank accounts (id, bank_code, bank_name, account_number, account_name). |
projects | Array | Projects opened for the client (id, name, mode). Retrieve only. |
verification | Object | url, expires_at and emailed of the attempt just started. Only in the create and verification-link responses. |
created_at | Date | When the client was registered. |
Onboarding status
status | Meaning |
|---|---|
not_started | The client has not opened its link and submitted anything. |
in_review | Filling in or submitted, awaiting a decision. |
incomplete | Verified by TrustAPI; the QPay merchant and bank account are being set up. |
verified | Every step done. |
rejected | Rejected. Sending the link again starts a new attempt. |
expired | The link expired. Send it again. |
Create a client
POST /v1/clients| Parameter | Type | Required | Description |
|---|---|---|---|
email | String | true | The client's email. The verification link goes there. |
client_reference_id | String | false | Your own ID for the client. Up to 48 characters, unique per team. |
The client is created, its verification starts on TrustAPI and the link is emailed. verification.url in the response is that link: show it in your own onboarding page. A second client with the same email or client_reference_id is refused (422).
If the verification service does not answer
The client is still created, with verification set to null. Wait a moment and start it with the verification link request.
Example request
BYL_TEAM_TOKEN="your team token"
curl -X POST https://byl.mn/api/v1/clients \
-H "Authorization: Bearer $BYL_TEAM_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"email": "[email protected]",
"client_reference_id": "shop_42"
}'Example response
{
"data": {
"id": 118,
"client_reference_id": "shop_42",
"name": null,
"type": null,
"contact_email": "[email protected]",
"phone": null,
"status": "in_review",
"is_verified": false,
"verified_at": null,
"verification_status": "pending",
"steps": [
{ "key": "trustapi", "status": "in_progress", "error": null },
{ "key": "qpay", "status": "pending", "error": null },
{ "key": "bank", "status": "pending", "error": null }
],
"qpay_merchant_id": null,
"bank_accounts": [],
"verification": {
"url": "https://trustapi.dotly.mn/verify/tok_8f2a…",
"expires_at": "2026-11-04T09:00:00+08:00",
"emailed": true
},
"created_at": "2026-10-28T09:00:00.000000Z",
"updated_at": "2026-10-28T09:00:00.000000Z"
}
}Retrieve a client
GET /v1/clients/:client_id
GET /v1/clients/by-client-reference-id/:client_reference_idThe response includes the client's projects. Each retrieve re-checks the status with TrustAPI, so it is right even when a webhook was missed. Another team's client returns 404.
curl https://byl.mn/api/v1/clients/by-client-reference-id/shop_42 \
-H "Authorization: Bearer $BYL_TEAM_TOKEN" \
-H 'Accept: application/json'List clients
GET /v1/clients| Parameter | Type | Description |
|---|---|---|
status | String | Filter by onboarding status. |
per_page | Number | Page size (default 25, at most 100). |
Newest first, paginated (meta.total, links.next).
Send the verification link
POST /v1/clients/:client_id/verification-linkWhen the client lost its link, the link expired or the attempt was rejected, this creates a (new) link and emails it; the response carries verification.url. It is refused with 409 when:
error | Meaning |
|---|---|
already_verified | The client is already verified. |
in_review | Submitted and awaiting a decision: nothing is left to fill in. |
onboarding_in_progress | Verified by TrustAPI; the remaining steps are completing automatically. |
When the verification service does not answer: 503 verification_unavailable.
The Project object
| Field | Type | Description |
|---|---|---|
id | Number | Project ID, used on the project endpoints. |
name | String | Name. |
mode | String | Test or Live. |
business_id | Number | The client the project runs under. null for the team's own projects. |
email | String | The project's contact email. |
qpay_merchant_id | String | The QPay merchant linked to the project; linked automatically once the client is verified. |
payout_bank_account | Object | The account the project pays out to (id, bank_code, bank_name, account_number, account_name, is_active), or null. |
created_at | Date | Created at. |
Create a project
POST /v1/projects| Parameter | Type | Required | Description |
|---|---|---|---|
name | String | true | Project name. Up to 255 characters. |
business_id | Number | false | The client's id. Omit for a project under your own business. |
A verified client's QPay merchant is linked to the new project right away; otherwise it is linked once the client is verified. A team without a plan can have one project (422 subscription_required); at the plan's limit, 422 project_limit_reached.
curl -X POST https://byl.mn/api/v1/projects \
-H "Authorization: Bearer $BYL_TEAM_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{ "name": "Shop 42", "business_id": 118 }'{
"data": {
"id": 57,
"name": "Shop 42",
"mode": "Test",
"business_id": 118,
"email": "[email protected]",
"qpay_merchant_id": null,
"payout_bank_account": null,
"created_at": "2026-10-28T09:05:00.000000Z",
"updated_at": "2026-10-28T09:05:00.000000Z"
}
}Retrieve and list projects
GET /v1/projects/:project_id
GET /v1/projectsThe list returns every project of the team (yours and your clients'), newest first; business_id narrows it to one client. per_page as above.
Payout account
GET /v1/projects/:project_id/bank-accounts
PUT /v1/projects/:project_id/bank-accountGET lists the active accounts of the business the project runs under (the client's); the account the client gave TrustAPI is added automatically when it is verified. PUT with bank_account_id makes one of them the project's payout account; another business's account is refused with 422. A payout account is required before going live.
Switch the mode
PUT /v1/projects/:project_id/mode| Parameter | Type | Required | Description |
|---|---|---|---|
mode | String | true | Test or Live. |
Going live needs a verified business, an active plan on the team and a payout account on the project; the error of the 422 names what is missing: business_not_verified, subscription_required, payout_account_required. The current mode is accepted as a no-op 200.
Working in a client's project
Once a project is open, call its checkout, invoice and customer endpoints with the same team token, with the returned id as :project_id. To receive the events of every project in one place, register a team webhook endpoint: the business_id of each payload tells which client it belongs to.