Tino Developers
View .md

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

ApproachDescriptionUse it for
APIyou call TinoPay to query data (transaction history, bank list) and generate QR codeschecks, reconciliation, showing a payment QR
WebhookTinoPay sends transaction data to your URL as soon as it happensinstant payment confirmation, no polling

Use webhooks to receive transactions instantly and the transaction history for reconciliation or as a fallback.

Basics

ItemValue
Base URLhttps://pay.tino.vn
PrefixAPI endpoints live under /api, e.g. https://pay.tino.vn/api/transactions; the QR image is at https://pay.tino.vn/img-qr
FormatJSON, UTF-8
CurrencyVND, integers, no decimal part
AuthenticationAuthorization: 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

CaseHTTPBody
missing or malformed Authorization header401{"message": "Thiếu API Token. Vui lòng gửi header Authorization: Bearer {token}."}
token does not exist or was revoked401{"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_to for another range.
  • Sorted by transaction_date descending (newest first), paginated.

Query parameters, all optional:

ParameterTypeDefaultDescription
qstring—search (LIKE) in reference_number, transaction_content, transaction_id
bank_account_idstring (UUID)—filter on one bank account; the UUID must belong to the token's owner
typestring—in incoming, out outgoing
transaction_date_fromdate (Y-m-d)todaystart date (inclusive)
transaction_date_todate (Y-m-d)todayend date (inclusive); at most 90 days after from
per_pageint (1–100)20records per page
pageint1current page

Date range:

  • Neither from nor to: today.
  • Only one of them: exactly that whole day.
  • from must be less than or equal to to, 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[]TypeDescription
idinttransaction ID
reference_numberstring | nullthe bank's reference code (core reference)
transaction_contentstringtransaction description
transaction_idstringtransaction code
bank_account_idstring (UUID)the bank account of the transaction
virtual_numberstring | nullvirtual account number, if any
amountintamount (VND)
typeint1 incoming, 2 outgoing
type_labelstringin incoming, out outgoing
statusinttransaction status
transaction_datestring (Y-m-d H:i:s)when the transaction actually happened
created_atstring (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.

ErrorHTTPBody
from after to422{"message": "transaction_date_from phải nhỏ hơn hoặc bằng transaction_date_to."}
range over 90 days422{"message": "Khoảng thời gian tối đa là 90 ngày."}
wrong parameter type422Laravel validation error: {"message": …, "errors": {…}}

Bank list

GET https://pay.tino.vn/api/banks

Banks 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" }
]
FieldTypeDescription
idintbank ID in TinoPay
codestringshort code (e.g. BIDV, MB), used as bank_code for QR codes
binstringbank identification number (BIN) of the Napas / VietQR standard
namestringfull 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-qr

Generates 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 /api prefix.
  • Returns image/png with the header Access-Control-Allow-Origin: *, so it works from browsers.
  • Codes follow the VietQR / Napas 247 standard (fast transfer service QRIBFTTA).
ParameterRequiredDescription
bank_codeyesbeneficiary bank code (e.g. BIDV, MB) from the bank list
account_noyesbeneficiary account number
account_namenoaccount holder name, shown on the framed image
amountnoamount (VND); applied only between 1,000 and 9,999,999,999,999
memonotransfer description, up to 25 characters (longer values are ignored)
templatenodefault framed image (default), only-qr QR code only
is_maskno1: 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=1

QR code only, no frame:

https://pay.tino.vn/img-qr?bank_code=BIDV&account_no=96240000001&amount=150000&memo=DH12345&template=only-qr

A missing or invalid required parameter returns text, not an image:

CaseReturns
bank_code invalid or unknownVui lòng nhập đúng mã ngân hàng
account_no missingVui 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.