Libraries
There is no SDK to install — the API is plain REST over HTTPS. Copy one of these tiny clients into your project and you have typed, reusable access in a few lines.
One dependency, at most
The Node and Python clients use the runtime’s built-in HTTP (Node 18+
fetch) or the ubiquitous requests package. PHP uses cURL from the standard library. Keep your key in an environment variable, never in code.A reusable client
Each client centralizes the base URL, auth header, and error handling so your calls stay one-liners.
// mywa.js — works on Node 18+ (built-in fetch), Bun, Deno
const BASE = process.env.MYWA_API ?? 'https://api.mywa.net/api/public/v1';
const KEY = process.env.MYWA_KEY;
async function request(method, path, body) {
const res = await fetch(BASE + path, {
method,
headers: {
Authorization: `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
const err = data.error ?? {};
throw new Error(`MYWA ${res.status} ${err.code ?? ''}: ${err.message ?? res.statusText}`);
}
return data;
}
export const mywa = {
get: (path) => request('GET', path),
post: (path, body) => request('POST', path, body),
};Using it
Once the client exists, every endpoint is a one-liner. Here’s sending an OTP and then checking its delivery status.
import { mywa } from './mywa.js';
// Send an OTP
const msg = await mywa.post('/messages', {
to: '+15551234567',
type: 'template',
template: { name: 'otp_code', variables: ['483920'] },
});
console.log('sent', msg.id);
// Later: check status
const status = await mywa.get(`/messages/${msg.id}`);
console.log(status.status); // "delivered"Starting automations
List the automations you can start, start one with vars its steps can use, then check the run. See Automations for every field.
import { mywa } from './mywa.js';
// What can I start?
const { automations } = await mywa.get('/automations');
const followUp = automations.find((a) => a.name === 'Order follow-up');
// Start it — steps can use {{vars.hook.orderId}}
const { runId } = await mywa.post(`/automations/${followUp.id}/enroll`, {
phone: '+15551234567',
vars: { orderId: 'A-1049', amount: 49.99 },
});
// Later: how did it go?
const run = await mywa.get(`/automation-runs/${runId}`);
console.log(run.status, run.goal?.name ?? 'no goal yet');Retry with backoff
Wrap sends so 429 and 5xx retry automatically. Never retry 4xx other than 429.
async function withRetry(fn, tries = 4) {
for (let i = 0; i < tries; i++) {
try {
return await fn();
} catch (e) {
const status = Number(String(e.message).match(/MYWA (\d+)/)?.[1]);
const retryable = status === 429 || status >= 500;
if (!retryable || i === tries - 1) throw e;
await new Promise((r) => setTimeout(r, 2 ** i * 500));
}
}
}
await withRetry(() =>
mywa.post('/messages', { to, type: 'template', template }),
);Ready for real flows?
The Use cases guide builds complete OTP, abandoned-cart, order-update, and automation flows on top of these clients.