Error codes
The error format, what each code means, rate limits, and how to fix each one.
Errors use a standard HTTP status and a JSON body with a machine-readable code and a human-readable message:
{
"error": {
"code": "invalid_phone",
"message": "phone must be an 8-digit Mongolian number"
}
}Branch on code, not on message. Messages may change.
Codes
| HTTP | code | What happened | What to do |
|---|---|---|---|
| 400 | invalid_request | The body is not valid JSON, or a field is missing or malformed. Also returned when the widget state is not 1 to 128 characters of A-Z a-z 0-9 . _ ~ - | Check the request against the API reference |
| 400 | invalid_phone | The phone number (also the widget phone option) is not a valid 8-digit Mongolian number | Send 8 digits; +976 and 976 prefixes are accepted |
| 401 | unauthorized | The API key is missing, invalid, revoked, or used from an IP address outside its allowlist | Send Authorization: Bearer sk_... with an active key; if the key has an IP allowlist, add your server's IP |
| 403 | client_suspended | Your account is suspended | Contact support |
| 403 | test_only | The endpoint works only with test keys | Use an sk_test_ key, for example for /v1/test/mo |
| 409 | rule_limit | The client already has the maximum number of live rules (5) | Delete a rule you no longer need, or email hello@2fa.mn to have the limit raised |
| 404 | not_found | The object doesn't exist in this mode | Check the id and that you use the API key of the same mode |
| 409 | prefix_taken | A live rule prefix overlaps another client's prefix. Prefixes are shared across 150073, so none may be a prefix of another (HIHI blocks HI and HIHI2) | Pick a different prefix |
| 429 | rate_limited | Too many requests | Back off and retry later; see Limits |
| 400 | invalid_token | The widget token is malformed or not recognised (POST /v1/verifications, or GET /v1/verifications/{token}) | Pass the token from the browser unchanged |
| 409 | token_used | The widget token was already redeemed | A token works once. To verify again, start the widget again |
| 410 | token_expired | The widget token has expired | Redeem the token soon after verification, or verify again |
| 403 | origin_not_allowed | The widget was loaded from a site not on the publishable API key's list | Add your site's origin under Dashboard > Widget |
| 403 | widget_disabled | The widget is turned off | Turn it on under Dashboard > Widget. If it is on, contact support |
| 500 | internal_error | A temporary problem on our side | Retry after a short wait; if it keeps happening, email hello@2fa.mn with the request time |
Not an error
Sending the code of an already verified session again is treated as already_verified. It is not an error: no new webhook is sent, and the SMS does not count toward that number's wrong-SMS throttle.
Rate limits
See Limits for when 429 rate_limited is returned.
Tips
- A
404for an object you just created usually means you're mixing test and live API keys. - Retry
429and5xxresponses with backoff. Don't retry other4xxresponses unchanged. - Log the
codetogether with your request id to make support faster.
What's next
Was this page helpful?