Synthetic monitoring for verification codes. otp-watch hands you a real UK phone number or a real email inbox, you trigger your own OTP send to it, and it tells you whether the code arrived and how long it took. Uptime monitors check that your API answered; this checks that the message actually showed up.
Get a key once, then each check is two calls:
curl -X POST https://otpwatch.flo-voice1.com/keys \
-H 'content-type: application/json' -d '{"email":"you@example.com"}'
# => {"key":"..."}
curl -X POST https://otpwatch.flo-voice1.com/checks \
-H 'authorization: Bearer <key>' -H 'content-type: application/json' \
-d '{"channel":"email"}'
# => {"id":"...","status":"pending","target":"a1b2c3d4e5f6@receivemail.dev",...}
Make your app send its verification email to target, then poll:
curl https://otpwatch.flo-voice1.com/checks/<id> -H 'authorization: Bearer <key>'
# => {"status":"received","latencyMs":4210,...} or {"status":"timed_out",...}
A real UK mobile number on a physical SIM (EE network), not a VoIP or virtual number, so it receives codes the way your users' phones do. SMS checks work the same way as email: {"channel":"sms"}, then send your code to the returned target.
GET /usage shows what's left, and after that POST /checks answers 402.POST /monitors/checkout, which returns a Stripe Checkout link.120 seconds by default. Pass timeoutSeconds anywhere from 10 to 600 when you create the check. A check that hasn't received anything by then becomes timed_out, which is the signal your pipeline should fail on.
otp-watch has no scheduler of its own: your CI or cron calls it on whatever interval you want and fails on timed_out, so your existing alerting does the paging. A minimal shell step:
ID=$(curl -s -X POST https://otpwatch.flo-voice1.com/checks -H "authorization: Bearer $OTP_WATCH_KEY" \
-H 'content-type: application/json' -d '{"channel":"email"}' | jq -r .id)
# ...trigger your app's verification email to the check's target here...
until S=$(curl -s https://otpwatch.flo-voice1.com/checks/$ID -H "authorization: Bearer $OTP_WATCH_KEY" | jq -r .status); [ "$S" != pending ]; do sleep 5; done
[ "$S" = received ] || exit 1
For frequent SMS checks use a monitor (POST /monitors/{id}/checks) so every run reuses the same number. To test an OTP integration without paying for a real SMS on every run, see testing OTP integrations without burning SMS credits.
An uptime monitor tells you your API returned 200. It can't tell you whether your SMS provider or email sender actually delivered the code, which is what breaks logins in practice: an expired sender ID, a template flagged as spam, a provider outage that still returns 200. otp-watch checks that last step.