SMS activation API

Two ways in: an endpoint compatible with the sms-activate protocol, so existing software switches by changing the URL and key, and a JSON REST API for new integrations. Both use the same API key from your panel.

Get your API key

Create an account, open the panel and generate a key under API access. Keep it secret: anyone with the key can spend your balance.

Sign up

Typical flow

  1. Call getNumber with a service and country.
  2. Use the number in the target app.
  3. Poll getStatus every few seconds until you get STATUS_OK.
  4. Call setStatus with 6 to finish, or 8 to cancel if no code came.

sms-activate compatible endpoint

Send GET or POST requests with api_key and action parameters. Responses are plain text, exactly like sms-activate.

https://smsrota.com/stubs/handler_api.php?api_key=KEY&action=getNumber&service=wa&country=187

Actions

ActionParametersResponse
getBalance—ACCESS_BALANCE:12.5000
getPricesservice?, country?{"187":{"wa":{"cost":0.4,"count":1563}}}
getNumberservice, country, maxPrice?ACCESS_NUMBER:48151623:15550123456
getStatusidSTATUS_WAIT_CODE · STATUS_WAIT_RETRY:code · STATUS_OK:code · STATUS_CANCEL
setStatusid, statusACCESS_RETRY_GET · ACCESS_ACTIVATION · ACCESS_CANCEL
getRentNumberservice, country, rent_time (4/24/72/168/720){"status":"success","phone":{"id":31,"endDate":"…","number":"447700900001"}}
getRentStatusid{"status":"success","quantity":"1","values":{"0":{"phoneFrom":"WhatsApp","text":"…"}}}
setRentStatusid, status (1 = finish, 2 = cancel){"status":"success"}
continueRentNumberid, rent_time{"status":"success","phone":{…}}

setStatus values

3
Request another code on the same number
6
Finish the activation
8
Cancel and refund (allowed two minutes after buying)

JSON REST API

Send the key in the Authorization header as "Bearer KEY". Amounts are strings in US dollars with four decimals.

GET   https://smsrota.com/v1/balance
GET   https://smsrota.com/v1/prices?service=wa
POST  https://smsrota.com/v1/activations  {"service":"wa","country":"187"}
GET   https://smsrota.com/v1/activations/:id
POST  https://smsrota.com/v1/activations/:id/retry
POST  https://smsrota.com/v1/activations/:id/complete
POST  https://smsrota.com/v1/activations/:id/cancel
GET   https://smsrota.com/v1/rent/prices?service=wa&hours=24
POST  https://smsrota.com/v1/rentals  {"service":"full","country":"16","hours":24}
GET   https://smsrota.com/v1/rentals/:id
POST  https://smsrota.com/v1/rentals/:id/extend  {"hours":24}
POST  https://smsrota.com/v1/rentals/:id/finish
POST  https://smsrota.com/v1/rentals/:id/cancel
POST  https://smsrota.com/v1/rentals/:id/dispute  {"reason":"no_sms"}
PUT   https://smsrota.com/v1/webhook  {"url":"https://example.com/sms-webhook"}
POST  https://smsrota.com/v1/webhook/test
GET   https://smsrota.com/v1/webhook/events

Webhooks

Set a URL under API access in your panel. We send a POST with a JSON body for each event. Answer with any 2xx status within 10 seconds; otherwise we try again after 1, 5, 30, 120, 360 and 720 minutes.

activation.sms_received
A code arrived for a number you bought.
activation.cancelled
A number was cancelled or expired and the money went back to your balance.
rental.sms_received
An SMS arrived on a rented number.
rental.ended
A rental ended or was cancelled.

Example body

{
  "id": "evt_1042",
  "type": "activation.sms_received",
  "createdAt": "2026-11-01T10:00:00.000Z",
  "data": {
    "activationId": 48151623,
    "service": "wa",
    "country": "187",
    "phone": "15550123456",
    "code": "123456"
  }
}

Checking the signature

Every request has an X-Webhook-Signature header like t=1730000000,v1=…, where v1 is the hex HMAC-SHA256 of "t.body" made with your signing secret. Reject a request if the signature doesn't match or t is more than 5 minutes old.

import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the request body exactly as received, before JSON parsing.
function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Errors

BAD_KEY
The API key is wrong or was replaced.
NO_NUMBERS
No stock for this service and country right now.
NO_BALANCE
Your balance is too low for this number.
EARLY_CANCEL_DENIED
Cancelling is possible two minutes after buying.
NO_ACTIVATION
The activation id doesn't exist or isn't yours.