Skip to content
SMSRay

OTP

Let SMSRay generate, send and check verification codes. You never store the code, and abuse limits are enforced for you.

Send an OTP

POST/sms/otp/send
https://api.smsray.in/api/sms/v1/sms/otp/send

Generate a 6-digit code, send it as a branded one-segment SMS, and store only its SHA-256 hash. The code is valid for 5 minutes.

Request

Headers

Send an OTP request headers
NameTypeRequiredRules
x-api-keystringrequiredYour secret key, ls_live_ followed by 48 hex characters.
content-typestringrequiredapplication/json
Idempotency-Keystringoptional1–200 printable ASCII characters. Safe retries for 24 h. See Idempotency.

Body parameters (JSON)

Send an OTP body parameters
NameTypeRequiredRules
tostringrequiredNepal mobile number (98XXXXXXXX, 97…, 96…, 977-prefixed accepted).
purposestringoptionalUp to 50 characters. Defaults to default. Codes are scoped to (your client, to, purpose), so login and reset-password never collide.
ipstringoptionalYour end user's IP address, up to 64 characters. Enables the per-IP limit (10 OTPs per 10 minutes).
senderIdstringoptionalOptional; same meaning as on /sms/send.

Example request

curl https://api.smsray.in/api/sms/v1/sms/otp/send \
  -H "x-api-key: $SMSRAY_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "to": "9779801234567", "purpose": "login", "ip": "203.0.113.7" }'

Response

200

200 application/json
{
  "success": true,
  "otpId": "9d5c0b7e-3a1f-4e62-8b1d-6a4f2c9e0d17",
  "messageId": "4f8a2c61-0b9e-4d3a-a7c5-1e6f9b2d8c40",
  "expiresInSeconds": 300
}

429Resend too soon

429 application/json
{
  "error": "Please wait before requesting another code",
  "code": "rate_limited"
}

Errors

Every error uses the same shape: { "error", "code", "details"? }. Branch on code, not on the message.

Send an OTP errors
HTTPcodeWhen
400invalid_requestValidation failed, or the number is not a Nepal mobile.
402insufficient_balanceNot enough balance for one OTP-rate segment.
401unauthorizedThe x-api-key header is missing ("Missing x-api-key") or the key is unknown ("Invalid API key").
403client_disabledThe API client that owns this key has been disabled in the dashboard.
403forbiddenOTP messages are not allowed for your workspace.
403workspace_pendingYour workspace has not been activated by Lacspace yet, so it cannot send.
403workspace_suspendedYour workspace is suspended.
409idempotency_conflictThe same Idempotency-Key was used with a different body, or the first request is still running (Retry-After: 1).
429rate_limitedResend cooldown (60 s per number and purpose), 3 OTPs per number per 10 minutes, 10 per end-user IP per 10 minutes, 60 requests per minute per calling IP, or your per-client TPS. Retry-After is set whenever it is known.
503unavailableNo sending route is available right now.
500server_errorSomething went wrong on our side. The body never contains a stack trace.

Notes

  • The SMS reads: <App>: 482913 is your verification code. Valid for 5 minutes. Do not share it. <App> is your API client's brand name (GSM-7, up to 30 characters, set in the dashboard).
  • The message is billed at your workspace's OTP rate and always fits in one GSM-7 segment.
  • The code is never stored or shown in plain text. In the dashboard and in GET /sms/messages/:id the text appears as (OTP hidden).
  • The per-number limit counts across every SMSRay customer, so one phone number cannot be flooded with codes from many apps.
  • OTPs skip pacing deferral: if every SIM is paced, the code moves to the next route at once rather than waiting.

Verify an OTP

POST/sms/otp/verify
https://api.smsray.in/api/sms/v1/sms/otp/verify

Check the code your user typed against the latest active code for the same number and purpose. Codes are single use.

Request

Headers

Verify an OTP request headers
NameTypeRequiredRules
x-api-keystringrequiredYour secret key, ls_live_ followed by 48 hex characters.
content-typestringrequiredapplication/json
Idempotency-Keystringoptional1–200 printable ASCII characters. Safe retries for 24 h. See Idempotency.

Body parameters (JSON)

Verify an OTP body parameters
NameTypeRequiredRules
tostringrequiredThe same number you sent the code to.
codestringrequiredThe 6 digits your user entered.
purposestringoptionalMust match the purpose used on send. Defaults to default.

Example request

curl https://api.smsray.in/api/sms/v1/sms/otp/verify \
  -H "x-api-key: $SMSRAY_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "to": "9779801234567", "purpose": "login", "code": "482913" }'

Response

200Correct code

200 application/json
{ "success": true, "verified": true }

200Wrong code

200 application/json
{ "success": true, "verified": false, "reason": "invalid_code" }

Response fields

Verify an OTP response fields
NameTypeRequiredRules
verifiedbooleanrequiredtrue once, for the correct code. The code is then consumed.
reasonstringoptionalPresent when verified is false: invalid_code, too_many_attempts (5 wrong tries) or no_active_otp (none sent, expired or already used).

Errors

Every error uses the same shape: { "error", "code", "details"? }. Branch on code, not on the message.

Verify an OTP errors
HTTPcodeWhen
400invalid_requestA required field is missing or malformed.
401unauthorizedThe x-api-key header is missing ("Missing x-api-key") or the key is unknown ("Invalid API key").
403client_disabledThe API client that owns this key has been disabled in the dashboard.
403workspace_pendingYour workspace has not been activated by Lacspace yet, so it cannot send.
403workspace_suspendedYour workspace is suspended.
409idempotency_conflictThe same Idempotency-Key was used with a different body, or the first request is still running (Retry-After: 1).
429rate_limitedYou exceeded your per-client request rate (default 20 requests per second). Honour Retry-After.
500server_errorSomething went wrong on our side. The body never contains a stack trace.

Notes

  • A wrong code is not an HTTP error. Always read verified; never treat a 200 as success on its own.
  • Each wrong code counts as an attempt. After 5 wrong attempts the code is locked and every further check returns too_many_attempts — send a new code.
  • Codes expire 5 minutes after they are sent and are deleted automatically afterwards.