Official Node.js SDK for EngageLab OTP.
Zero dependencies. Node.js 14+. TypeScript declarations included.
npm install engagelab-otpconst { OTPClient } = require('engagelab-otp');
const otp = new OTPClient(
process.env.ENGAGELAB_DEV_KEY,
process.env.ENGAGELAB_DEV_SECRET
);
// Platform-generated OTP — easiest path
const { message_id } = await otp.send('+6591234567', 'your-template-id', {}, 'en');
const { verified } = await otp.verify(message_id, userTypedCode);| Mode | Method | When to use |
|---|---|---|
| Platform-generated | otp.send() |
EngageLab generates and stores the code. You only call verify() later. Simplest path. |
| Caller-generated | otp.sendCustom() |
You generate the code yourself. EngageLab is just the carrier. Use when you need the code in your own DB. |
// Platform-generated
const r = await otp.send('+6591234567', 'tpl-id', { name: 'Alice' }, 'en');
const v = await otp.verify(r.message_id, '123456');
// Caller-generated
await otp.sendCustom('+6591234567', 'custom-tpl', { code: '482910', name: 'Alice' });EngageLab signs callbacks with X-CALLBACK-ID (HMAC-SHA256). The SDK verifies signatures and parses events for you.
Whitelist source IPs in your firewall:
119.8.170.74,114.119.180.30
const express = require('express');
const { WebhookVerifier } = require('engagelab-otp');
const app = express();
app.use(express.json());
const verifier = new WebhookVerifier({
username: process.env.ENGAGELAB_WEBHOOK_USERNAME,
secret: process.env.ENGAGELAB_WEBHOOK_SECRET,
});
app.post('/webhook', verifier.middleware(async (events) => {
for (const e of events) {
if (e.kind !== 'message_status') continue;
if (!e.is_terminal) {
// mid-flight (e.g. fallback in progress) — wait, do not act
continue;
}
if (e.status === 'delivered') await markDelivered(e.message_id);
else if (e.status === 'verified') await markVerified(e.message_id);
else await handleFailure(e);
}
}));parseEvents() returns one of four typed objects:
kind |
When | Key fields |
|---|---|---|
message_status |
per-message lifecycle | message_id, status, is_terminal, current_send_channel, error_code |
notification |
account-level alert | event (insufficient_balance, …), data |
uplink |
inbound user reply | from, to, body |
system_event |
console action audit | event (account_login, …) |
plan · target_valid · target_invalid
sent · sent_failed
delivered · delivered_failed
verified · verified_failed · verified_timeout
is_terminal === true for: delivered, delivered_failed, sent_failed, verified*, target_invalid.
const { EngagelabError } = require('engagelab-otp');
try {
await otp.send('+6591234567', 'tpl', {});
} catch (err) {
if (err instanceof EngagelabError) {
if (err.retryable) {
// HTTP 429/5xx, or API codes 1000/5001/5016
// → exponential backoff
} else {
// Permanent failure — fix your call or notify the user
console.log(err.code, err.httpStatus, err.message);
}
}
}See examples/ for runnable code:
01-send-and-verify.js— platform-generated OTP, full flow02-send-custom.js— caller-generated code, single + bulk03-webhook-express.js— receive callbacks via Express middleware04-error-handling.js— retry strategy and error categorization
npm testMIT