# 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](/en/docs/tinopay-start#calling-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](https://pay.tino.vn/docs).

## 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](/en/docs/tinopay-webhook) to receive transactions instantly and the [transaction history](#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 |

```bash
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:

```http
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](/en/docs/api/tinopay-post-api-tokens); see [API tokens](/en/docs/tinopay-start#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:

```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):

```php
$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

```http
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:

| 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 `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.

```bash
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"
```

```javascript
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`:

```json
{
  "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

```http
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](#vietqr-codes). No authentication. Returns a JSON array, not wrapped in `data`.

```bash
curl "https://pay.tino.vn/api/banks" -H "Accept: application/json"
```

```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

```http
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).

| Parameter | Required | Description |
| --- | --- | --- |
| `bank_code` | yes | beneficiary bank code (e.g. `BIDV`, `MB`) from the [bank list](#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 |

```html
<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:

```text
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:

```text
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:

| 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](/en/docs/tinopay-webhook) receives the transaction with this description (and `code` when it matches your [payment codes](/en/docs/api/tinopay-patch-merchant)), so you can match the order and confirm payment automatically.

## Support

Stuck on an integration: open a support ticket from the TinoPay dashboard.
