Upgrade from API v1

Detailed comparison between legacy userapi and API v2 to help plan your migration.


Changes from Legacy (userapi/)

AspectLegacy (userapi/)API v2 (/v2/)
Base URLhttps://my.sepay.vn/userapi/https://userapi.sepay.vn/v2
Authentication errorsHTTP 200, empty bodyHTTP 401, JSON body
Business errorsHTTP 200HTTP 422 with error_code
Response keytransactions, bankaccounts, count_transactionsAlways data
IdentifiersNumeric auto-incrementUUID
Currency typefloat (string)integer
Paginationlimit (default 5000), since_idpage/per_page (default 20, max 100), since_id for polling
Amount filtersExact match (amount_in=X)Range (amount_in_min/amount_in_max)
Date filterstransaction_date_min/_maxtransaction_date_from/_to
Rate limit headerx-sepay-userapi-retry-afterRetry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
Information

Legacy endpoints (userapi/*) remain unchanged and continue to work normally. You can use both legacy and API v2 in parallel during migration.


Endpoint Mapping

V1V2Note
GET /userapi/transactions/listGET /v2/transactions
GET /userapi/transactions/details/{id}GET /v2/transactions/{uuid}Numeric ID -> UUID
GET /userapi/transactions/count(removed)Use pagination.total
GET /userapi/bankaccounts/listGET /v2/bank-accounts
GET /userapi/bankaccounts/details/{id}GET /v2/bank-accounts/{uuid}Numeric ID -> UUID
GET /userapi/bankaccounts/count(removed)Use pagination.total
GET /userapi/bidv/{id}/ordersGET /v2/bank-accounts/{uuid}/orders
POST /userapi/bidv/{id}/ordersPOST /v2/bank-accounts/{uuid}/orders
GET /userapi/bidv/{id}/orders/{oid}GET /v2/bank-accounts/{uuid}/orders/{uuid}
DELETE /userapi/bidv/{id}/orders/{oid}DELETE /v2/bank-accounts/{uuid}/orders/{uuid}
POST /userapi/bidv/{id}/orders/{oid}/vasPOST /v2/bank-accounts/{uuid}/orders/{uuid}/va
DELETE /userapi/bidv/{id}/orders/{oid}/vas/{va}DELETE /v2/bank-accounts/{uuid}/orders/{uuid}/va/{va}
(none)GET /v2/bank-accounts/{uuid}/vaNew: Virtual Accounts API

Migration Steps

Update Base URL

Change https://my.sepay.vn/userapi/ to https://userapi.sepay.vn/v2.

Switch from Numeric IDs to UUIDs

All v2 endpoints use UUIDs instead of numeric IDs. Get UUIDs from list responses or save them when creating new resources.

Update amount handling (float -> integer)

JSComparison
1
2
V1: "amount_in": "18067000.00" (string, float)
V2: "amount_in": 18067000 (integer)

Remove float/string parsing functions when processing amounts from v2.

Update pagination (limit -> page/per_page)

V1 uses limit (default 5000). V2 uses page/per_page (default 20, max 100). To fetch all data, iterate through pages until has_more: false.

Update error handling

V1 always returns HTTP 200; you need to parse the body to detect errors. V2 returns proper HTTP status codes (401, 404, 422, 429). Check the HTTP status before parsing the body.


Comparison Examples

List transactions:

V1
1
2
curl -X GET "https://my.sepay.vn/userapi/transactions/list?limit=20" \
-H "Authorization: Bearer YOUR_TOKEN"
V2
1
2
curl -X GET "https://userapi.sepay.vn/v2/transactions?per_page=20" \
-H "Authorization: Bearer YOUR_TOKEN"

Response format:

V1 - Root key varies by endpoint
1
2
3
4
5
6
7
8
9
{
"status": 200,
"messages": {
"success": true
},
"transactions": [
...
]
}
V2 - Always uses "data"
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"status": "success",
"data": [
...
],
"meta": {
"pagination": {
"total": 150,
"per_page": 20,
"has_more": true
}
}
}

Handling IDs

API v2 uses UUIDs for all identifiers. Numeric IDs from v1 do not work with v2 endpoints.

Example:

  • V1: GET /userapi/transactions/details/12345
  • V2: GET /v2/transactions/a1b2c3d4-e5f6-7890-abcd-ef1234567890

To get the corresponding UUID, call the v2 list endpoint and map based on business fields (account number, reference number, etc.).


Pagination

V1V2
Parameterslimitpage, per_page
Default5000 records20 records/page
Maximum5000100 records/page
Pollingsince_id (numeric)since_id (UUID)
Check for more dataCompare returned count with limitmeta.pagination.has_more

To fetch all data, increment page until has_more: false:

Example
1
2
3
4
5
# Page 1
GET /v2/transactions?page=1&per_page=100
# Page 2 (if has_more: true)
GET /v2/transactions?page=2&per_page=100