Skip to content
Documentation menu

Documentation

Security

Your own anti-abuse rules on top of the platform limits: per-IP and per-app rate limits, recipient spread, IP checks, daily caps and IP lists.

The panel’s Security page holds your own anti-abuse rules, on top of the limits we run for everyone. They can only tighten: nothing here loosens a platform limit, and a rule you never touch costs nothing. New apps start with the app rate limit and the per-IP rate limit on; existing apps are unchanged until you turn a rule on.

Three modes

Every rule runs in one of three modes.

ModeWhat happens
OffThe rule is not evaluated.
MonitorThe rule is evaluated; every hit is counted under “Would refuse” and listed in Recent events, but the send goes out and no email is sent. Use it to see what a limit would do before it does it.
EnforceThe send is refused. Your backend gets a 429 or 403 (see below) and the message is never sent or billed.

Start a rule in Monitor, watch Recent events for a day, then switch to Enforce. “Apply recommended” fills in values sized for a login form; adjust to your traffic before saving.

The rules

RuleWhat it does
App rate limitHow many codes the whole app may start per minute and per hour. Stops a burst from many addresses at once. On for every new app (60 a minute, 1,000 an hour); raise it before you grow past that.
Per-IP rate limitCodes one end user’s IP may start per minute and per hour, whatever its reputation. On for every new app (5 a minute, 30 an hour). Needs client_ip on the send.
Per-IP recipient spreadHow many different phone numbers or emails one IP may reach in an hour. A real user reaches one or two; a pump reaches a new number every time. Three is a good value.
Require client_ipRefuse a send that does not carry the end user’s IP. Turn it on once every send does, so an integration mistake cannot switch the IP rules off.
Block VPN, proxy and Tor / Block datacenter IPsRefuse end users behind an anonymising network or on a cloud address, which real phones rarely use. The platform already slows both down to one code a minute; this refuses them.
Per-recipient limitTighter than the platform limit for one number or email: codes per hour and seconds between two codes. Both limits apply; the stricter wins.
Daily capsA ceiling per UTC day for this app: sends, spend in USD, or both. 0 switches that half off. The hard backstop if everything else is tuned wrong.
IP listsOne IP or CIDR range per line. A denied address is always refused. An allowed address skips the IP rules above, and only those; the app-wide and recipient rules still apply.

What your backend sees

StatusErrorMeaning
429RateLimitExceededErrorA counting rule is full (app, IP, spread, recipient, daily). The message ends in policy; platform limits name their bucket instead (recipient, company, key, cooldown, client_ip). Honour Retry-After and let the user try again.
403SendPolicyBlockedErrorAn absolute rule refused the send: a deny-listed IP, a missing client_ip, or a blocked IP type. Do not retry the same request.

The response never names the rule. You configured it, so the Recent events table on the Security page tells you which one fired, with the IP, the country and the masked recipient; a caller holding a leaked key learns nothing. See Error codes.

Three things to know

  • The IP rules only see sends that carry client_ip. Pass the end user’s address in POST /otp/send; the page shows what share of your last seven days did.
  • Every hit is kept for 30 days, refused or monitored, so a limit can be tuned against what actually happened rather than guessed.
  • Owners get one email per app per day, on the first refusal, when the switch at the bottom of the page is on. Country restrictions are not here: they live on Routes (see Channels & routing).