Audience: Merchant Open API integrators
Related APIs:
# Document Method Path 1 Request File Upload URL POST/v1/files/upload-url2 Create Virtual Account POST/v1/virtual-account/sub/create3 Get VA Detail GET/v1/virtual-account/sub/get4 Open Virtual Account Status Webhook —
Important — A 200response from Create Virtual Account means the application was accepted, not that the account exists. No account number is returned at submit time. Do not present the account to your end user, and do not instruct anyone to remit funds, until the account reachesSUCCESS.
fileId.{
"documentType": "VA_ONBOARDING",
"fileType": "pdf",
"fileSize": 284517,
"currency": "VND"
}{
"status": 200,
"msg": "OK",
"code": null,
"data": {
"fileId": "0f1c9a2e-5b34-4f0e-9a7a-1c2d3e4f5a6b",
"uploadUrl": "https://s3.ap-southeast-1.amazonaws.com/bpn-files/files/10023/20260915/0f1c9a2e-5b34-4f0e-9a7a-1c2d3e4f5a6b.pdf?X-Amz-Algorithm=...&X-Amz-Signature=...",
"expiresAt": "2026-09-15T07:12:44Z"
},
"traceId": "8f0a5b8c4e1f4d0e8f0a5b8c4e1f4d0e"
}HTTP PUT the raw file body to uploadUrl. The signature is already embedded in the query string, so do not attach your Open API credentials or any Authorization header. A 200 from storage means the object is stored.fileId inside companyInfo.documents[].files[] and companyInfo.persons[].documents[].files[]. The internal storage key is never exposed.| Rule | Value | Enforced at |
|---|---|---|
documentType | VA_ONBOARDING | Upload URL request |
currency | VND | Upload URL request |
Accepted fileType | pdf, jpg, jpeg, png, heic | Upload URL request |
Declared fileSize | Optional. When supplied it must be between 10 KB and 10 MB | Upload URL request |
| Upload URL validity | 10 minutes from issue (expiresAt) | Object storage |
| Real file type | Detected from the file header, not from the extension; must match the fileType you declared | Create Sub VA |
| Real single file size | ≤ 10 MB (10485760 bytes) | Create Sub VA |
| Real total submission size | ≤ 40 MB (41943040 bytes) across all files in one application | Create Sub VA |
Warning — An expired uploadUrlcannot be renewed. If the PUT did not complete beforeexpiresAt, request a new URL and use the newfileId. AfileIdwhose object was never uploaded fails the application.
fileId belongs to the merchant account that requested it. Referencing another merchant's fileId fails the application.| Field | Required | Rule |
|---|---|---|
currency | Yes | VND |
accountSubType | Yes | STATIC |
userType | Yes | Individual or Corporate |
individualInfo | When userType=Individual | Object, must be non-empty |
companyInfo | When userType=Corporate | Object, must be non-empty |
primaryAccountId | No | When omitted, routing is resolved from currency |
Note — Field naming. VND structured fields use camelCase ( legalName,dateOfBirth,fileId). snake_case keys (legal_name,date_of_birth,file_id) are still accepted as a legacy fallback, but camelCase wins when both are present. New integrations should use camelCase only.
YYYY-MM-DD. dateOfBirth, incorporationDate, and any supplied document dateOfIssue / dateOfExpiry are parsed strictly, so 2026-02-30 is rejected.individualInfo| Field | Required | Rule / values |
|---|---|---|
firstName | Yes | Combined with lastName into the full name on the application |
lastName | Yes | |
dateOfBirth | Yes | Strict YYYY-MM-DD |
email | No | |
phone | No | |
address | Yes | Full residential address |
idNumber | Yes | Citizen ID number |
idType | Yes | 1 = Citizen ID. The only value accepted on the individual path — a passport cannot be used to open an individual account |
sex | Yes | 1 = Male, 2 = Female, 3 = Other |
nationality | Yes | Country code |
dateOfIssue | Yes | Citizen ID issue date |
dateOfExpiry | Yes | Citizen ID expiry date |
Note — On the individual path, idType,sex,dateOfIssueanddateOfExpiryare only checked for presence and type before submission —idTypeandsexmust parse as integers, and the two dates only need to be non-empty. A value outside the ranges above, including anidTypeother than1, is therefore not rejected immediately; it surfaces later as a rejected application. Send the values in the formats above.
companyInfodocuments[] (company evidence), and persons[] (legal representative and directors).| Field | Required | Rule / values |
|---|---|---|
legalName | Yes | Registered legal name |
registrationNumber | Yes | Business registration number |
incorporationCountry | Yes | ISO 3166-1 alpha-2 (e.g. VN); alpha-3 also accepted |
incorporationDate | Yes | Strict YYYY-MM-DD |
address | Yes | Registered address |
email | No | |
phone | No | |
website | No | |
natureOfBusiness | Yes | Business description |
geographicScope | Yes | Array of country codes, e.g. ["VN", "SG"] |
documents[] — company evidence| Field | Required | Rule |
|---|---|---|
type | Yes | See the table below |
files | Yes | Array of { "fileId": "..." } (a bare fileId string is also accepted). 1–10 entries |
documentNumber | Conditional | Mandatory for type=4 |
dateOfIssue | No | Strict YYYY-MM-DD when supplied |
dateOfExpiry | No | Strict YYYY-MM-DD when supplied |
type | Document | Mandatory |
|---|---|---|
1 | Incorporation paper | Yes |
2 | Business registration extract | No |
3 | Nature and scope declaration | Yes |
4 | Import / export licence | No — but documentNumber is mandatory if present |
5 | Product images | No |
9 | Other | No |
type merges their files into one document.persons[] — legal representative and directors| Field | Required | Rule / values |
|---|---|---|
role | Yes | 1 = Legal representative, 2 = Director |
firstName | Yes | Combined with lastName into the full name on the application |
lastName | Yes | |
dateOfBirth | Yes | Strict YYYY-MM-DD |
email | No | |
phone | No | |
address | Yes | |
idNumber | Yes | |
idType | Yes | 1 = Citizen ID, 2 = Passport |
sex | Yes | 1 = Male, 2 = Female, 3 = Other |
nationality | Yes | Country code |
documents | Yes | Exactly one identity document — see below |
{
"documents": [
{
"dateOfIssue": "2019-03-14",
"dateOfExpiry": "2029-03-13",
"files": [{ "fileId": "..." }, { "fileId": "..." }]
}
]
}idType (1 → Citizen ID, 2 → Passport); do not set it yourself. documentNumber is taken from the person's idNumber. If you send several document objects for one person, all their files are merged into that single identity document and the first non-empty dateOfIssue / dateOfExpiry wins — send one object to keep the result predictable.role, idType and sex inside persons[] are range-checked before submission and an out-of-range value fails the request immediately.persons[] must contain one and only one entry with role=1. Zero or two both fail.nationality differs from companyInfo.incorporationCountry must have idType=2. Country codes are normalised to ISO alpha-3 before the comparison, so VN and VNM are treated as the same country.type=1 and type=3 must both be present with at least one file.type=4 document without documentNumber fails.| Limit | Value |
|---|---|
| Persons per application | 20 |
| Documents per application (company + all person identity documents) | 30 |
| Files per document | 10 |
| Single file size | 10 MB |
| Total size of all files in one application | 40 MB |
eventId and treat the event as a prompt to query rather than as the source of truth.GET /v1/virtual-account/sub/get) with the virtualAccountId from the create response, and read status:status | Meaning | Merchant action |
|---|---|---|
INIT | Application accepted, not yet submitted for review | Wait |
PROCESSING | Account under review | Wait |
SUCCESS | Account created and usable | Receiving details are available; the account may now receive funds |
FAIL | Application rejected, or account creation failed | Read failReason, correct, submit a new application |
code: "10000046" with msg: "invalid parameter". The message does not say which field failed, so use the list below to diagnose. Each entry is a rule that, when broken, produces that generic error:dateOfBirth or incorporationDate is not a valid YYYY-MM-DD date, or a supplied document dateOfIssue / dateOfExpiry is not.role, idType or sex is not an integer.persons[]role is not 1 or 2.idType is not 1 or 2.sex is not 1, 2 or 3.persons[] is empty, or has more than 20 entries.persons[] does not contain exactly one role=1.nationality differs from incorporationCountry does not use idType=2.type=1 or type=3 company document is missing or has no file.type=4 document has no documentNumber.files[], or more than 10 files.fileId is unknown, or belongs to another merchant.fileType you declared, or cannot be detected.fileIds.Note — Include the response traceIdwhen raising a support ticket; it identifies the exact request and the rule that failed.