Automating OTP Verification in Node.js
AltiviaCloud Team · August 29, 2026 · 3 min read
A complete flow — order a number, wait for the code, close the order — with the retry and cleanup logic that tutorials usually skip.
Automated tests that touch a real signup flow need a real phone number. Here is the whole loop in Node, including the parts that matter when it runs unattended at three in the morning.
Before you start
You need an API key from the dashboard and some balance. Every request authenticates with a bearer token:
const API = "https://api.altiviacloud.com.br/v1";
const headers = {
Authorization: `Bearer ${process.env.ALTIVIA_API_KEY}`,
"Content-Type": "application/json",
};
Keep the key in the environment. A key committed to a repository is a key you will rotate in a hurry later.
Step 1 — order a number
async function orderNumber(countryId, serviceId) {
const res = await fetch(`${API}/orders`, {
method: "POST",
headers,
body: JSON.stringify({ countryId, serviceId }),
});
const body = await res.json();
if (!res.ok) {
throw new Error(body.error?.message ?? `Order failed (${res.status})`);
}
return body.data; // { id, phoneNumber, expiresAt, ... }
}
Read body.error.code rather than matching on the message text — messages get reworded, codes do not. Two you will meet in practice:
INSUFFICIENT_BALANCE— top up before retrying; retrying immediately just fails again.RATE_LIMITED— you exceeded the per-minute cap. TheRetry-Afterheader tells you how long to wait.
Step 2 — wait for the code
Polling is the simplest thing that works, as long as it respects the expiry the API already gave you:
async function waitForCode(orderId, expiresAt) {
const deadline = new Date(expiresAt).getTime();
while (Date.now() < deadline) {
const res = await fetch(`${API}/orders/${orderId}/sms`, { headers });
const { data } = await res.json();
const code = data?.find((sms) => sms.code)?.code;
if (code) return code;
await new Promise((r) => setTimeout(r, 3000));
}
return null; // nada chegou dentro da janela
}
Three seconds is a reasonable interval. Polling every 200ms will not make the carrier faster; it will only spend your rate limit.
Note the loop bounds itself on expiresAt instead of a fixed number of attempts. The API is the authority on how long the number lives, so let it be.