TinoPay public API
TinoPay's public API runs at https://pay.tino.vn: query transaction history, list banks and generate VietQR code images. It is separate from the merchant API (https://api.tino.vn/service/<id>/tinopay/…, Tino sign-in token), which manages the merchant, bank accounts and webhooks.
Updated: 2026-10-25. Source: the TinoPay documentation.
Two ways to integrate
| Approach | Description | Use it for |
|---|---|---|
| API | you call TinoPay to query data (transaction history, bank list) and generate QR codes | checks, reconciliation, showing a payment QR |
| Webhook | TinoPay sends transaction data to your URL as soon as it happens | instant payment confirmation, no polling |
Use webhooks to receive transactions instantly and the transaction history for reconciliation or as a fallback.
Basics
| Item | Value |
|---|---|
| Base URL | https://pay.tino.vn |
| Prefix | API endpoints live under /api, e.g. https://pay.tino.vn/api/transactions; the QR image is at https://pay.tino.vn/img-qr |
| Format | JSON, UTF-8 |
| Currency | VND, integers, no decimal part |
| Authentication | Authorization: Bearer <api_token>, except the bank list and the QR image |
curl -G "https://pay.tino.vn/api/transactions" \
-H "Authorization: Bearer $TINOPAY_API_TOKEN" \
-H "Accept: application/json"Authentication
Every request that needs authentication sends an API token in the Authorization header with the Bearer scheme:
Authorization: Bearer <api_token>Creating an API token
- In the TinoPay web app: menu Tích hợp (Integrations) → API Access (
/api-access), click Tạo API Token (Create API Token), give it a memorable name (e.g.server-production) and copy the token right away. - With the merchant API: Create an API token; see API tokens in Getting started.
The raw token is shown only once, at creation. If you lose it, revoke the old token and create a new one. You can create several tokens and revoke any of them on the API Access page.
Access scope
- Each token belongs to one owner account and only reads that owner's data (transactions, bank accounts).
- A parameter pointing at a resource you do not own (e.g. someone else's
bank_account_id) returns no data.
Calling examples
JavaScript:
const res = await fetch("https://pay.tino.vn/api/transactions", {
headers: { Authorization: `Bearer ${process.env.TINOPAY_API_TOKEN}`, Accept: "application/json" },
});
const data = await res.json();PHP (Laravel):
$response = Http::withToken(env('TINOPAY_API_TOKEN'))
->acceptJson()
->get('https://pay.tino.vn/api/transactions');
$data = $response->json();Authentication errors
| Case | HTTP | Body |
|---|---|---|
missing or malformed Authorization header | 401 | {"message": "Thiếu API Token. Vui lòng gửi header Authorization: Bearer {token}."} |
| token does not exist or was revoked | 401 | {"message": "API Token không hợp lệ."} |
The message texts are in Vietnamese, as the API returns them.
Security
- TinoPay stores tokens as SHA-256 hashes, never the raw token: the raw token can be seen only once, at creation.
- Every valid request updates the token's last-used time (
last_used_at), so you can see which tokens are in use. - Never put a token in client code (browser, mobile app) or commit it to Git: use it on servers only.
- Revoke a leaked token at once on the API Access page; a revoked token returns 401 from the next request.
Transaction history
GET https://pay.tino.vn/api/transactions- Authentication: required.
- Returns transactions of every bank account owned by the token's owner.
- Defaults to today's transactions; pass
transaction_date_from,transaction_date_tofor another range. - Sorted by
transaction_datedescending (newest first), paginated.
Query parameters, all optional:
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | — | search (LIKE) in reference_number, transaction_content, transaction_id |
bank_account_id | string (UUID) | — | filter on one bank account; the UUID must belong to the token's owner |
type | string | — | in incoming, out outgoing |
transaction_date_from | date (Y-m-d) | today | start date (inclusive) |
transaction_date_to | date (Y-m-d) | today | end date (inclusive); at most 90 days after from |
per_page | int (1–100) | 20 | records per page |
page | int | 1 | current page |
Date range:
- Neither
fromnorto: today. - Only one of them: exactly that whole day.
frommust be less than or equal toto, at most 90 days apart.
curl -G "https://pay.tino.vn/api/transactions" \
-H "Authorization: Bearer $TINOPAY_API_TOKEN" \
-H "Accept: application/json" \
--data-urlencode "q=DH12345" \
--data-urlencode "type=in" \
--data-urlencode "transaction_date_from=2026-10-01" \
--data-urlencode "transaction_date_to=2026-10-20" \
--data-urlencode "per_page=20"const params = new URLSearchParams({ q: "DH12345", type: "in", transaction_date_from: "2026-10-01", transaction_date_to: "2026-10-20", per_page: "20" });
const res = await fetch(`https://pay.tino.vn/api/transactions?${params}`, {
headers: { Authorization: `Bearer ${process.env.TINOPAY_API_TOKEN}`, Accept: "application/json" },
});
const json = await res.json();Response 200:
{
"data": [
{
"id": 1032,
"reference_number": "0551dTCO-82LiomOss",
"transaction_content": "DH12345 chuyen tien mua hang",
"transaction_id": "20261020083338000006",
"bank_account_id": "9c1e6f2a-5b7d-4e1a-9f3c-2d8b6a4e0c11",
"virtual_number": "9624QA01",
"amount": 150000,
"type": 1,
"type_label": "in",
"status": 0,
"transaction_date": "2026-10-20 08:33:38",
"created_at": "2026-10-20 08:33:40"
}
],
"links": {
"first": "https://pay.tino.vn/api/transactions?page=1",
"last": "https://pay.tino.vn/api/transactions?page=3",
"prev": null,
"next": "https://pay.tino.vn/api/transactions?page=2"
},
"meta": { "current_page": 1, "from": 1, "last_page": 3, "per_page": 20, "to": 20, "total": 57 }
}Field in data[] | Type | Description |
|---|---|---|
id | int | transaction ID |
reference_number | string | null | the bank's reference code (core reference) |
transaction_content | string | transaction description |
transaction_id | string | transaction code |
bank_account_id | string (UUID) | the bank account of the transaction |
virtual_number | string | null | virtual account number, if any |
amount | int | amount (VND) |
type | int | 1 incoming, 2 outgoing |
type_label | string | in incoming, out outgoing |
status | int | transaction status |
transaction_date | string (Y-m-d H:i:s) | when the transaction actually happened |
created_at | string (Y-m-d H:i:s) | when TinoPay recorded it |
links and meta are the Laravel API Resource pagination structure: page through with meta.last_page or links.next.
| Error | HTTP | Body |
|---|---|---|
from after to | 422 | {"message": "transaction_date_from phải nhỏ hơn hoặc bằng transaction_date_to."} |
| range over 90 days | 422 | {"message": "Khoảng thời gian tối đa là 90 ngày."} |
| wrong parameter type | 422 | Laravel validation error: {"message": …, "errors": {…}} |
Bank list
GET https://pay.tino.vn/api/banksBanks TinoPay supports, with the bank code (code) and BIN: use it for a bank picker or to get the code for VietQR codes. No authentication. Returns a JSON array, not wrapped in data.
curl "https://pay.tino.vn/api/banks" -H "Accept: application/json"[
{ "id": 1, "code": "BIDV", "bin": "970418", "name": "Ngân hàng TMCP Đầu tư và Phát triển Việt Nam", "created_at": "2025-01-01 00:00:00", "updated_at": "2025-01-01 00:00:00" },
{ "id": 2, "code": "MB", "bin": "970422", "name": "Ngân hàng TMCP Quân đội", "created_at": "2025-01-01 00:00:00", "updated_at": "2025-01-01 00:00:00" }
]| Field | Type | Description |
|---|---|---|
id | int | bank ID in TinoPay |
code | string | short code (e.g. BIDV, MB), used as bank_code for QR codes |
bin | string | bank identification number (BIN) of the Napas / VietQR standard |
name | string | full bank name |
The list mirrors the bank data configured in TinoPay: use the code values from it as-is when calling other APIs.
VietQR codes
GET https://pay.tino.vn/img-qrGenerates a dynamic VietQR code for customers to scan and pay. The endpoint returns a PNG image directly, so you can put it in an <img> tag without drawing the QR yourself.
- No authentication. No
/apiprefix. - Returns
image/pngwith the headerAccess-Control-Allow-Origin: *, so it works from browsers. - Codes follow the VietQR / Napas 247 standard (fast transfer service QRIBFTTA).
| Parameter | Required | Description |
|---|---|---|
bank_code | yes | beneficiary bank code (e.g. BIDV, MB) from the bank list |
account_no | yes | beneficiary account number |
account_name | no | account holder name, shown on the framed image |
amount | no | amount (VND); applied only between 1,000 and 9,999,999,999,999 |
memo | no | transfer description, up to 25 characters (longer values are ignored) |
template | no | default framed image (default), only-qr QR code only |
is_mask | no | 1: partly mask the account number on the framed image |
<img src="https://pay.tino.vn/img-qr?bank_code=BIDV&account_no=96240000001&amount=150000&memo=DH12345" alt="Scan to pay" width="320">Every parameter:
https://pay.tino.vn/img-qr?bank_code=BIDV&account_no=96240000001&account_name=CONG%20TY%20ABC&amount=150000&memo=DH12345&template=default&is_mask=1QR code only, no frame:
https://pay.tino.vn/img-qr?bank_code=BIDV&account_no=96240000001&amount=150000&memo=DH12345&template=only-qrA missing or invalid required parameter returns text, not an image:
| Case | Returns |
|---|---|
bank_code invalid or unknown | Vui lòng nhập đúng mã ngân hàng |
account_no missing | Vui lòng nhập số tài khoản |
In a payment flow, set memo to a unique order code (e.g. DH12345). When the customer pays, the webhook receives the transaction with this description (and code when it matches your payment codes), so you can match the order and confirm payment automatically.
Support
Stuck on an integration: open a support ticket from the TinoPay dashboard.