wenova-link
v2.0.1
Published
WENOVA Link SDK — SMS/OTP, address, currency format, and Lao time plugins
Maintainers
Readme
wenova-link
All-in-one SDK for WENOVA Link — SMS/OTP, Lao address, currency format, and Lao time plugins.
For single-feature packages, see also wenova-sms-otp and wenova-address.
Installation
npm i wenova-linkUsage
const {
sendOtp,
getProvinces,
getProvinceById,
getDistrictsByProvince,
getDistrictById,
getVillagesByDistrict,
getVillageById,
currencyFormat,
currencyAllAmount,
currencyList,
laoTimeFormat,
laoTimeFormatByYear,
laoTimeFormatByMonthIndex,
laoTimeFormatByMonthName,
} = require('wenova-link');SMS / OTP
Use either API Token or Script ID (at least one required).
await sendOtp({
header: 'WNV-OTP',
phoneNumber: '2012345678',
message: 'Your OTP is: 123456',
token: 'your-token',
usePackage: true,
});Address (plugin key required)
const pluginKey = 'your-plugin-key';
const provinces = await getProvinces({ pluginKey, kw: 'vien' });
const districts = await getDistrictsByProvince(1, { pluginKey });
const villages = await getVillagesByDistrict(1, { pluginKey });Currency format (plugin key required)
await currencyFormat({ code: 'LAK', amount: '1000', key: 'your-plugin-key' });
await currencyAllAmount({ amount: '5000', key: 'your-plugin-key' });
await currencyList({ code: 'LAK', field: 'price', items: [...], key: 'your-plugin-key' });Lao time (plugin key required)
await laoTimeFormat({ data: '18-06-2026', key: 'your-plugin-key' });
await laoTimeFormatByYear({ year: '2026', key: 'your-plugin-key' });
await laoTimeFormatByMonthIndex({ index: '01', key: 'your-plugin-key' });
await laoTimeFormatByMonthName({ name: 'January', key: 'your-plugin-key' });API
sendOtp(data)
Sends SMS/OTP via Wenova API (POST /sms/package).
data.header(string): SMS header (e.g.WNV-OTP)data.phoneNumber(string): 10 digits starting with20data.message(string): Message contentdata.token(string, optional): API tokendata.scriptId(number, optional): Script IDdata.usePackage(boolean):true= deduct from SMS packagedata.baseUrl(string, optional): API base URL
Address
getProvinces({ pluginKey, kw?, baseUrl?, lang? })getProvinceById(id, { pluginKey, baseUrl?, lang? })getDistrictsByProvince(provinceId, { pluginKey, kw?, baseUrl?, lang? })getDistrictById(id, { pluginKey, baseUrl?, lang? })getVillagesByDistrict(districtId, { pluginKey, kw?, baseUrl?, lang? })getVillageById(id, { pluginKey, baseUrl?, lang? })
Currency
currencyFormat({ code, amount, key })currencyAllAmount({ amount, key })currencyList({ code, field, items, key })
For LAK, formatted is Lao Kip text:
{
"raw": 1000,
"formatted": "ໜຶ່ງພັນກີບຖ້ວນ",
"code": "LAK",
"symbol": "₭"
}Lao time
laoTimeFormat({ data, key })laoTimeFormatByYear({ year, key })laoTimeFormatByMonthIndex({ index, key })laoTimeFormatByMonthName({ name, key })
All functions return a Promise with the API response.
Throws: WenovaApiError when validation fails or the API returns a non-success response.
Error handling
Failed API calls throw WenovaApiError with structured fields parsed from the Wenova API body.
const { sendOtp, WenovaApiError } = require('wenova-link');
try {
await sendOtp({
header: 'WNV-OTP',
phoneNumber: '2012345678',
message: 'Your OTP is: 123456',
token: 'invalid-token',
usePackage: true,
});
} catch (err) {
if (err instanceof WenovaApiError) {
console.error(err.message); // e.g. "[30307] SMS API token not found"
console.error(err.code); // 30307 (Wenova 5-digit business code)
console.error(err.statusCode); // HTTP status (e.g. 404)
console.error(err.path); // API path
console.error(err.method); // HTTP method
console.error(err.response); // Raw error JSON from API
}
}Success response shape
{
"success": true,
"message": "Request successful",
"data": { },
"timestamp": "2026-06-18T12:00:00.000Z",
"statusCode": 200,
"path": "/sms/package",
"method": "POST"
}Error response shape
{
"statusCode": 400,
"timestamp": "2026-06-18T12:00:00.000Z",
"path": "/sms/package",
"method": "POST",
"message": "Either token or scriptId is required",
"code": 30101
}
codeis the Wenova 5-digit business id (not HTTP status). Format:XX(service) +Y(category) +ZZ(sequence).
Common error codes
Wenova code format: XX (service) + Y (category) + ZZ (sequence).
| Code | Message | |------|---------| | SMS (30xxx) | | | 30101 | Either token or scriptId is required | | 30102 | Links are not allowed in SMS messages | | 30105 | SMS package quota is insufficient | | 30106 | Wallet balance is insufficient for SMS segments | | 30107 | User must be verified before sending SMS | | 30108 | User account is not active | | 30302 | Wallet not found for user | | 30303 | SMS package not found | | 30307 | SMS API token not found | | 30308 | SMS scriptId not found | | 30501 | SMS rate limit exceeded | | 30901 | SMS gateway returned an error | | Address (60xxx) | | | 60101 | Plugin key (key) query parameter is required | | 60102 | Plugin key is valid but not enabled for address API | | 60103 | User plugin subscription is not active | | 60104 | Invalid province, district, or village id in URL | | 60301 | Province not found | | 60302 | District not found | | 60303 | Village not found | | Currency format (60xxx) | | | 60101 | Plugin key (key) is required | | 60102 | Plugin key is valid but not enabled for currency API | | 60103 | User plugin subscription is not active | | 60304 | Currency format with code not found (e.g. invalid ISO code) | | 90101 | Request validation failed (missing code, amount, field, or items) | | Lao time (60xxx) | | | 60101 | Plugin key (key) is required | | 60102 | Plugin key is valid but not enabled for lao-time API | | 60103 | User plugin subscription is not active | | 60105 | Invalid date format (expected DD-MM-YYYY) | | 60106 | Invalid month index (01–12) or month name | | Common / system (90xxx) | | | 90101 | Request validation failed | | 30501 | Rate limit exceeded | | 90901 | Internal server error |
Plugin endpoints (address, currency, lao-time) share the 60xxx range. Use
60101–60103for key/auth issues and603xxfor resource-not-found cases.
Exported error helpers
WenovaApiError— error class withcode,statusCode,path,method,responsegetWenovaErrorCode(data)— extract 5-digit code from API bodyextractMessageText(data)— extract human-readable messageformatErrorMessage(data)— message with[code]prefix when available
