01 · open source otp service

send otp on whatsapp with two api calls.

self-host it, or sign up free — 500 whatsapp codes a month, telegram unmetered.

code lifetime, in seconds

0 s150 s300 s
  • ttl300 s
  • attempts3
  • throttle5 / phone · hour

02 · how it works

one endpoint sends a code. one endpoint checks it.

both take json and return json. your api key rides in a header, never in a url, so it stays out of logs and referrers.

post /v1/otp/send

expires_in 300

curl -X POST "$WAOTP_API/v1/otp/send" \
  -H "X-Api-Key: $WAOTP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "919876543210", "channel": "whatsapp"}'

post /v1/otp/verify

verified true

curl -X POST "$WAOTP_API/v1/otp/verify" \
  -H "X-Api-Key: $WAOTP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "919876543210", "code": "123456"}'

03 · what it costs

500 whatsapp codes a month, free. telegram is unmetered.

the limits are data, not code. quota, expiry, attempts and throttles all live in one settings row you can edit without a redeploy.

500

free whatsapp codes a month

300 s

before a code expires

3

guesses, then it burns

04 · your data

codes and api keys are hashed. the phone number is not.

the one-time code and every api key go in as sha256 digests, so an operator reading the back office sees hashes, not secrets. the meta token and the telegram bot token are fernet-encrypted in the settings row. the phone number is not hashed, because the gateway has to match it on the next call.

05 · open source

run the whole thing on your own server.

one repository, two processes. bring your own meta and telegram credentials and the gateway never has to leave the machine you chose.

stack
python, fastapi and pocketbase. pocketbase is both the database and the back office, so there is no separate admin panel to build.
limits are data
quota, expiry, attempts and throttles live in a row you edit — not in code you redeploy.
providers
meta cloud api, or the telegram bot api. telegram is unmetered; whatsapp carries the 500 free sends a month.
dry run
set WAOTP_MOCK_DELIVERY=1 to fake delivery while every database row stays real, so the whole flow is testable with no provider credentials.

sign up and send a code, or clone it and run it yourself.