Package Information
Available Nodes
Documentation generating…
Documentation
@myotp/n8n-nodes-myotp
An n8n community node for MyOTP.App. Send one-time passcodes over SMS, WhatsApp and Telegram, verify the code the user typed, and check delivery, from inside a workflow.
One credential (your API key) and one node with six operations. No runtime dependencies.
Install
Two routes, depending on how you run n8n:
- Self-hosted, as the instance owner or an admin: go to Settings > Community Nodes, choose Install, enter
@myotp/n8n-nodes-myotpand confirm. The MyOTP node then appears in the node panel for everyone on the instance. - n8n Cloud, or self-hosted with verified community nodes enabled: once this package is verified by n8n, search for MyOTP in the node panel and install it from there. Until then, Cloud cannot install it.
Self-hosted from the command line:
cd ~/.n8n/nodes
npm install @myotp/n8n-nodes-myotp
Then restart n8n.
Credentials
The node uses one credential, MyOTP API, with a single field: the API key. It is sent as the X-API-Key header on every request. The credential test calls GET /me, so a wrong key or an IP that is not on the key's allowlist fails right there.
Where to get a key:
- Humans: sign up at https://myotp.app/sign-up/ (15 free credits, email and phone verification). Keys live under API Keys in the dashboard.
- Agents:
POST https://api.myotp.app/v1/agent/registerwith{"email": "..."}. The 201 body carries the key once. The balance starts at zero. To buy credits,POST /v1/topupwith{"credits": N}(25 to 50000, $0.02 each). The first call answers 402 with payment challenges (USDC or card); pay one and repeat the request. Details: https://myotp.app/developer-api/
Phone numbers are digits only in E.164 order, country code first, no plus sign. 14155550123, not +1 (415) 555-0123.
Operations
| Operation | Endpoint | Required | Optional |
|---|---|---|---|
| Send OTP | POST /generate_otp |
Phone Number | Channel (sms, whatsapp, telegram), OTP Length (4 to 8), OTP Validity (60 to 86400 s, SMS only), Template Order (1 to 5), Brand, Force Send, Return OTP |
| Verify OTP | POST /verify_otp |
OTP | Phone Number, Message ID |
| Extend OTP | POST /extend_otp |
Message ID, Duration (60 to 14400 s) | |
| Check OTP Status | POST /check_otp_status |
Message ID | |
| Account | GET /me |
||
| Usage Report | POST /report |
Start Date, End Date (YYYY-MM-DD), Page, Per Page (1 to 100) |
Optional fields left empty are not sent, so the API applies its own defaults. Force Send is sent as the string "true" or "false", which is what the API expects for that field. Return OTP is sent as a boolean.
The node runs once per input item and keeps the paired item, so you can map fields from a webhook body or a previous node with expressions. The node is marked usable as a tool for the AI Agent node.
Errors
Every rejected MyOTP request carries {"error": {"http_code": N, "message": "..."}}. The node reads that message and raises it as the n8n error, with the HTTP code attached. With Continue on Fail switched on, the item is output as { "error": "...", "http_code": "..." } instead.
Common codes: 400 bad input, 401 wrong key, 402 no balance, 403 calling IP not on the key's allowlist, 409 an unexpired OTP already exists for that number (set Force Send to override).
Example: webhook, send OTP, respond
Import examples/webhook-send-otp.json (Workflow menu > Import from File). Then:
- Open Webhook and create the Header Auth credential it asks for (a header name such as
X-Webhook-Keyand a long random value). The webhook rejects requests without it. - Open Send OTP and pick your MyOTP API credential.
The workflow is five nodes:
- Webhook (
POST /webhook/send-otp, Header Auth) receives{"phone_number": "14155550123", "channel": "sms"}. - IF checks
phone_numberagainst^[1-9][0-9]{6,14}$. Anything else answers 400 without touching MyOTP. - MyOTP > Send OTP with Phone Number from the body and Channel limited to
sms,whatsapportelegram. - Respond to Webhook returns
{"message_id": ..., "expires_at": ...}to the caller. - Respond 400 for the invalid-number branch.
A send endpoint spends your credits and sends messages to whatever number it is given, so never expose one without authentication. Keep the Header Auth on, and rate limit it as well: n8n has no per-webhook rate limit, so put the limit in the reverse proxy in front of n8n (for example nginx limit_req) or call the webhook only from your own backend.
Test it:
curl -X POST https://your-n8n.example.com/webhook/send-otp \
-H "X-Webhook-Key: your-long-random-value" \
-H "Content-Type: application/json" \
-d '{"phone_number": "14155550123", "channel": "sms"}'
Response:
{ "message_id": "8f14e45f-ea0d-4d1b-9c6a-2b7c1f0a9e33", "expires_at": "2026-09-04T12:05:00Z" }
To finish the flow, add a second webhook that takes {"otp": "123456", "message_id": "..."} and feeds MyOTP > Verify OTP. A status of success in its output means the user holds the phone.
Development
npm install
npm test # vitest: request building for each operation, error mapping, node wiring
npm run lint # eslint-plugin-n8n-nodes-base
npm run build # tsc + copy icons and codex json into dist/
To run n8n's community package scanner (@n8n/scan-community-package) on the built package before it is published, install the scanner in a scratch directory, npm pack this package, unpack the tarball and run node scripts/scan-package.mjs <unpacked-dir> <source-dir>. In Docker, from this folder's parent:
docker run --rm -v "$PWD:/w" -w /w node:22-alpine sh -c "npm ci && npm run build && mkdir -p /scan && cd /scan && npm init -y >/dev/null && npm install @n8n/scan-community-package@0.34.0 && cd /w && npm pack --pack-destination /scan && mkdir -p /scan/pkg /scan/src && tar xzf /scan/myotp-n8n-nodes-myotp-*.tgz -C /scan/pkg --strip-components=1 && cp -r credentials nodes package.json /scan/src/ && node scripts/scan-package.mjs /scan/pkg /scan/src"
The package publishes dist/ only. API reference: https://myotp.app/developer-api/ and the OpenAPI file at the root of this repository.
License
MIT