RelySMS
API · handler_api.php protocol

Numbers for receiving SMS — from your own software

If your software already works with an activation service over handler_api.php, it works with RelySMS: change the server address and the key. Same actions, same answers, our prices and refunds.

https://relysms.pro/stubs/handler_api.php

Quick start

  1. Sign in and open Balance → API → Create a key. The key is shown once — save it.
  2. Top up to $20 or more: purchases through the API go through only with that balance. Holding a key with a zero balance is fine.
  3. Point your software at https://relysms.pro/stubs/handler_api.php and use your key instead of the previous service's.
  4. Buy a number (getNumber), then poll the status every 5–10 seconds (getStatus) until STATUS_OK arrives with the code.
  5. Finish the order (setStatus=6). If no code comes, cancel after 2 minutes (setStatus=8) or just let the window end: the money comes back on its own.

Rules and limits

  • Purchases through the API need a balance of at least $20. Below that, getNumber answers NO_BALANCE; every other method works.
  • The number is yours for 20 minutes. No code — the order closes itself and the money returns to your balance.
  • You can cancel 2 minutes after buying if no code has come. The money comes back at once.
  • Once the first code arrives the order is paid and not refunded. While the window is open, further codes can still arrive.
  • Up to 20 requests and 5 purchases per second per key; up to 50 open orders at a time.
  • The price is fixed at purchase. maxPrice keeps you from buying above your limit.
  • The service terms apply: spam, fraud and mass sign-ups for those ends are forbidden.

Methods

Every request is a GET or POST to one address, with parameters in the query string or a form body. The key goes in api_key, the action in action.

getBalance

The account balance in dollars.

Only api_key and action.

GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=getBalance

Answer

ACCESS_BALANCE:25.00

getNumber

Buy a number for a service in a country. Charged at once; refunded if no code comes. Answer: ACCESS_NUMBER:order_number:phone.

ParameterRequiredDescription
serviceyesService code (getServicesList), e.g. tg.
countryyesCountry code (getCountries).
maxPricenoMaximum price in dollars. Above it — WRONG_MAX_PRICE with the current price, nothing charged.
GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=getNumber&service=tg&country=187&maxPrice=0.80

Answer

ACCESS_NUMBER:1042:15550001111

Possible errors: BAD_SERVICE, BAD_COUNTRY, NO_NUMBERS, NO_BALANCE, WRONG_MAX_PRICE:0.59, LIMIT_ACTIVATIONS, RATE_LIMIT, BANNED

getNumberV2

The same as getNumber, answered as JSON with the price and the window's times.

ParameterRequiredDescription
serviceyesService code.
countryyesCountry code.
maxPricenoMaximum price in dollars.
GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=getNumberV2&service=tg&country=187&maxPrice=0.80

Answer

{
  "activationId": 1042,
  "phoneNumber": "15550001111",
  "activationCost": 0.59,
  "countryCode": "187",
  "canGetAnotherSms": true,
  "activationTime": "2026-10-27T10:00:00.000Z",
  "activationEndTime": "2026-10-27T10:20:00.000Z"
}

Possible errors: BAD_SERVICE, BAD_COUNTRY, NO_NUMBERS, NO_BALANCE, WRONG_MAX_PRICE:0.59, LIMIT_ACTIVATIONS, RATE_LIMIT

getStatus

The order's status: waiting, code received (with the code), or cancelled.

ParameterRequiredDescription
idyesOrder number from getNumber.
GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=getStatus&id=1042

Answer

STATUS_OK:48213

Possible errors: NO_ACTIVATION

setStatus

Act on an order: ready, another code, finish, cancel (see Statuses).

ParameterRequiredDescription
idyesOrder number.
statusyes1, 3, 6 or 8.
GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=setStatus&id=1042&status=6

Answer

ACCESS_ACTIVATION

Possible errors: NO_ACTIVATION, BAD_STATUS, EARLY_CANCEL_DENIED, ERROR_SQL

getActiveActivations

Every open order of the key: number, status, codes and texts received.

Only api_key and action.

GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=getActiveActivations

Answer

{
  "status": "success",
  "activeActivations": [
    {
      "activationId": 1042,
      "serviceCode": "tg",
      "phoneNumber": "15550001111",
      "activationCost": 0.59,
      "activationStatus": "2",
      "smsCode": ["48213"],
      "smsText": ["Telegram code: 48213"],
      "activationTime": "2026-10-27T10:00:00.000Z",
      "countryCode": "187"
    }
  ]
}

getPrices

Our prices in dollars and roughly how many numbers. Without parameters — everything on sale.

ParameterRequiredDescription
servicenoOnly this service.
countrynoOnly this country.
GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=getPrices&service=tg&country=187

Answer

{ "187": { "tg": { "cost": 0.59, "count": 1000 } } }

Possible errors: BAD_SERVICE, BAD_COUNTRY

getCountries

Countries with protocol codes and names in Russian and English.

Only api_key and action.

GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=getCountries

Answer

{ "187": { "id": 187, "rus": "США", "eng": "United States", "visible": 1 } }

getServicesList

Services with protocol codes.

Only api_key and action.

GET https://relysms.pro/stubs/handler_api.php?api_key=KEY&action=getServicesList

Answer

{ "status": "success", "services": [{ "code": "tg", "name": "Telegram" }] }

Statuses

Values of status in setStatus:

  • status=1 → ACCESS_READY — The number is entered in the service, waiting for the code. Changes nothing; kept for compatibility.
  • status=3 → ACCESS_RETRY_GET — Ask for another code on the same number — after the first code, while the order is open. Free. Until it comes, getStatus answers STATUS_WAIT_RETRY.
  • status=6 → ACCESS_ACTIVATION — Finish the order after the code. No more SMS will reach the number.
  • status=8 → ACCESS_CANCEL — Cancel an order without a code (not before 2 minutes). The money returns to your balance.

getStatus answers:

  • STATUS_WAIT_CODE — No code yet — ask again in 5–10 seconds.
  • STATUS_OK:48213 — The code arrived; after the colon is the code (or the SMS text if it has no code).
  • STATUS_WAIT_RETRY:48213 — Another code was asked for and hasn't come yet; after the colon is the previous code.
  • STATUS_CANCEL — Cancelled or closed without a code; the money is already back.

Errors

Errors are plain strings, as is usual for this protocol. All but RATE_LIMIT come with status 200.

NO_KEYapi_key is missing.
BAD_KEYWrong or disabled key, or a blocked account.
BAD_ACTIONUnknown action.
BAD_SERVICENo such service code.
BAD_COUNTRYNo such country code.
NO_NUMBERSNo numbers for this pair right now; nothing charged.
NO_BALANCELess than $20 on the balance (the API floor), or less than the price.
WRONG_MAX_PRICEThe price is above maxPrice; the current price follows the colon.
LIMIT_ACTIVATIONSToo many open orders — finish or cancel some.
RATE_LIMITToo frequent; HTTP 429. Retry in a second.
BANNEDThe account is blocked — contact support.
NO_ACTIVATIONNo order with that number for this key.
BAD_STATUSThat action isn't available for the order now (e.g. finishing without a code).
EARLY_CANCEL_DENIEDCancelling is allowed 2 minutes after the purchase.
ERROR_SQLA temporary error on our side — repeat the request in a minute.

Service and country codes

The codes are the protocol's standard ones: tg — Telegram, wa — WhatsApp and so on; country 187 — the US. getServicesList and getCountries return the full lists — take codes from there, not from old tables: services and countries change with stock.

REST API v1

The same numbers and the same rules as handler_api.php, as JSON: services by name (telegram), countries by ISO code (US), HTTP statuses and error codes a program can branch on. Use it for new code; keep handler_api.php for software that already speaks it.

https://relysms.pro/api/v1

Key

Send the key in a header: Authorization: Bearer <key> (or X-Api-Key: <key>). Never in the URL — URLs end up in logs. The limits, the $20 floor and the catalogue are the same as for handler_api.php.

Retries without double purchases

Send Idempotency-Key with a value of your own (a UUID, up to 255 characters) on POST /orders and /orders/batch. If the connection drops and you repeat the request with the same key, you get the first answer back (with the header Idempotent-Replayed: true) and nothing is bought twice. The same key with a different body is refused; a repeat that arrives while the first is still running gets idempotency_in_progress. Successful answers are kept for 24 hours; a purchase that failed charged nothing and can be repeated with the same key.

Several numbers at once

POST /orders/batch buys up to 20 numbers for one service and country in one request — this is the way to buy faster than 5 a second. Purchases go one by one; the first that fails stops the batch, and the answer lists what was bought plus the reason in stoppedBy. If nothing was bought, the answer is that error.

Pages

GET /orders returns the newest first. When next is not null, pass it as before to get the next page.

Webhooks

Instead of polling GET /orders/{id}, set an HTTPS address with PUT /webhook and we POST to it when something happens to an order bought through the API. The body carries the order as it is at the moment of sending. Answer with any 2xx within 10 seconds; one order can produce several events, and the same event can come twice after a retry, so key on the delivery id.

  • order.code_received — A code arrived (each new one sends this again).
  • order.finished — The order was finished — by you, or when its time ran out after a code.
  • order.cancelled — Cancelled with no code; the money is back on the balance.
  • order.expired — Time ran out with no code; the money is back on the balance.
  • ping — A test from POST /webhook/test; order is null.
POST /relysms-hook HTTP/1.1
Content-Type: application/json
X-RelySMS-Event: order.code_received
X-RelySMS-Delivery: cm…
X-RelySMS-Signature: t=1793095272,v1=5f2b…

{
  "id": "cm…",
  "event": "order.code_received",
  "createdAt": "2026-10-27T10:01:12.000Z",
  "order": { "id": 1042, "status": "received", "codes": [{ "code": "48213", … }], … }
}

Every request carries X-RelySMS-Signature: t=<unix time>,v1=<signature>. The signature is HMAC-SHA256 of the string "t.body" (the raw body, byte for byte) with your webhook secret, in hex. Check it, and that t is within 5 minutes, before trusting the body.

Python

import hashlib, hmac, time

def verify(secret: str, body: bytes, header: str, tolerance=300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t, sig = parts.get("t", ""), parts.get("v1", "")
    if not t.isdigit() or abs(time.time() - int(t)) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

Node.js

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

export function verify(secret, rawBody, header, tolerance = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const t = Number(parts.t);
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > tolerance) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return expected.length === parts.v1?.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

If your server doesn't answer 2xx, we retry after 10 seconds, 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours — 7 attempts in all — then give up. GET /webhook/deliveries shows what was sent and what failed.

Endpoints

GET /balance

The balance in dollars, and whether it is enough to buy through the API.

curl -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/balance

Answer

{ "balance": 25, "currency": "USD", "canBuy": true, "apiMinimum": 20 }

GET /services

Every service on offer: our id, the name and the handler_api.php code.

ParameterRequiredDescription
lang querynoLanguage of the names: en (default), ru, uk, zh, es, pt, id, vi, tr, ar, fr.
curl -H "Authorization: Bearer $KEY" "https://relysms.pro/api/v1/services?lang=en"

Answer

{ "services": [{ "id": "telegram", "name": "Telegram", "protocolCode": "tg" }] }

GET /countries

Every country on offer: ISO code, name and the handler_api.php code.

ParameterRequiredDescription
lang querynoLanguage of the names: en (default), ru, uk, zh, es, pt, id, vi, tr, ar, fr.
curl -H "Authorization: Bearer $KEY" "https://relysms.pro/api/v1/countries?lang=en"

Answer

{ "countries": [{ "id": "US", "name": "United States", "protocolCode": 187 }] }

GET /prices

Our prices in dollars and roughly how many numbers are available. Without parameters, everything on sale.

ParameterRequiredDescription
service querynoService id from /services, e.g. telegram.
country querynoCountry ISO code from /countries, e.g. US.
curl -H "Authorization: Bearer $KEY" "https://relysms.pro/api/v1/prices?service=telegram"

Answer

{ "prices": [{ "service": "telegram", "country": "US", "price": 0.59, "stock": 1000 }] }

Possible errors: unknown_service, unknown_country

POST /orders

Buy a number. The money is charged at once and returned if no code arrives.

ParameterRequiredDescription
service bodyyesService id from /services, e.g. telegram.
country bodyyesCountry ISO code from /countries, e.g. US.
maxPrice bodynoHighest price in dollars, a number. Above it — price_above_max with the current price, nothing charged.
curl -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c1f0e2a-run-1" \
  -d '{"service":"telegram","country":"US","maxPrice":0.8}' \
  https://relysms.pro/api/v1/orders

Answer

{
  "order": {
    "id": 1042,
    "service": "telegram",
    "country": "US",
    "phone": "15550001111",
    "price": 0.59,
    "status": "waiting",
    "refunded": false,
    "codes": [],
    "awaitingAnotherCode": false,
    "createdAt": "2026-10-27T10:00:00.000Z",
    "expiresAt": "2026-10-27T10:20:00.000Z",
    "cancelableAt": "2026-10-27T10:02:00.000Z"
  }
}

Possible errors: bad_request, unknown_service, unknown_country, no_numbers, insufficient_balance, below_api_minimum, price_above_max, too_many_open_orders, buying_too_fast, idempotency_key_reused, idempotency_in_progress

POST /orders/batch

Buy up to 20 numbers for one service and country in one request.

ParameterRequiredDescription
service bodyyesService id from /services, e.g. telegram.
country bodyyesCountry ISO code from /countries, e.g. US.
quantity bodyyesHow many numbers, 1 to 20.
maxPrice bodynoHighest price in dollars, a number. Above it — price_above_max with the current price, nothing charged.
curl -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c1f0e2a-batch-1" \
  -d '{"service":"telegram","country":"US","quantity":5}' \
  https://relysms.pro/api/v1/orders/batch

Answer

{
  "orders": [{ "id": 1043, "phone": "15550001112", … }, { "id": 1044, … }],
  "requested": 5,
  "placed": 2,
  "stoppedBy": { "code": "no_numbers", "message": "…" }
}

Possible errors: bad_request, unknown_service, unknown_country, no_numbers, insufficient_balance, below_api_minimum, price_above_max, too_many_open_orders, buying_too_fast, idempotency_key_reused, idempotency_in_progress

GET /orders

Your orders, newest first: the open ones by default, or all.

ParameterRequiredDescription
status querynoopen (default) — waiting or with a code; all — every order.
limit querynoOrders per page, 1 to 100 (default 50).
before querynoThe next value from the previous page.
curl -H "Authorization: Bearer $KEY" "https://relysms.pro/api/v1/orders?status=all&limit=50"

Answer

{ "orders": [{ "id": 1042, … }], "next": "1001" }

Possible errors: bad_request

GET /orders/{id}

One order with its codes. Poll it every 5–10 seconds while the status is waiting.

ParameterRequiredDescription
id pathyesThe order id from the purchase answer.
curl -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/orders/1042

Answer

{
  "order": {
    "id": 1042,
    "status": "received",
    "codes": [{ "code": "48213", "text": "Telegram code: 48213", "receivedAt": "2026-10-27T10:01:12.000Z" }],
    …
  }
}

Possible errors: not_found

POST /orders/{id}/finish

Finish an order that has a code. The number will take no more SMS. Repeating is harmless.

ParameterRequiredDescription
id pathyesThe order id from the purchase answer.
curl -X POST -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/orders/1042/finish

Answer

{ "order": { "id": 1042, "status": "finished", … } }

Possible errors: not_found, invalid_state

POST /orders/{id}/another-code

Another code on the same number — after the first code, while the order is open. Free. Until it comes, the order has awaitingAnotherCode = true.

ParameterRequiredDescription
id pathyesThe order id from the purchase answer.
curl -X POST -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/orders/1042/another-code

Answer

{ "order": { "id": 1042, "status": "received", "awaitingAnotherCode": true, … } }

Possible errors: not_found, invalid_state, try_later

POST /orders/{id}/cancel

Cancel an order with no code, from 2 minutes after purchase. The money goes back at once.

ParameterRequiredDescription
id pathyesThe order id from the purchase answer.
curl -X POST -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/orders/1042/cancel

Answer

{ "order": { "id": 1042, "status": "cancelled", "refunded": true, … } }

Possible errors: not_found, invalid_state, too_early_to_cancel

GET /webhook

The webhook address and its signing secret, or null.

curl -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/webhook

Answer

{ "webhook": { "url": "https://example.com/relysms-hook", "secret": "whsec_…", "createdAt": "2026-10-27T10:00:00.000Z" } }

PUT /webhook

Set or change the address. The secret is made once and kept; rotateSecret makes a new one.

ParameterRequiredDescription
url bodyyesHTTPS address of your server. Private and local addresses are refused.
rotateSecret bodynotrue — make a new signing secret.
curl -X PUT -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/relysms-hook"}' \
  https://relysms.pro/api/v1/webhook

Answer

{ "webhook": { "url": "https://example.com/relysms-hook", "secret": "whsec_…", "createdAt": "2026-10-27T10:00:00.000Z" } }

Possible errors: bad_request, invalid_webhook_url

DELETE /webhook

Remove the webhook. Events not yet sent are dropped.

curl -X DELETE -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/webhook

Answer

{ "webhook": null }

POST /webhook/test

Send a ping event to check the address and the signature.

curl -X POST -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/webhook/test

Answer

{ "delivery": { "id": "cm…" } }

Possible errors: not_found

GET /webhook/deliveries

The last 20 deliveries: event, status, attempts, your server's last answer.

curl -H "Authorization: Bearer $KEY" https://relysms.pro/api/v1/webhook/deliveries

Answer

{
  "deliveries": [{
    "id": "cm…", "event": "order.code_received", "orderId": 1042,
    "status": "pending", "attempts": 2, "lastHttpStatus": 502, "lastError": "HTTP 502",
    "createdAt": "2026-10-27T10:01:12.000Z", "deliveredAt": null, "nextAttemptAt": "2026-10-27T10:02:22.000Z"
  }]
}

Order statuses

The status field of an order:

  • waiting — The number is yours and no code has come yet.
  • received — A code has come; it is in codes. More codes can come while the order is open.
  • finished — You finished the order (or its time ran out after a code).
  • cancelled — Cancelled with no code; refunded is true and the money is on the balance.
  • expired — Time ran out with no code; the money is back on the balance.

Errors

An error is an HTTP status and a JSON body with a code to branch on and a message for people. Some carry extra fields (price, cancelableAt, apiMinimum).

{ "error": { "code": "price_above_max", "message": "The price is above maxPrice.", "price": 0.59 } }
missing_key401No key in Authorization or X-Api-Key.
invalid_key401The key is wrong, was replaced, or the account is blocked.
bad_request400The body or a parameter is wrong; message says which.
unknown_service400No such service id.
unknown_country400No such country code.
idempotency_key_invalid400Idempotency-Key is empty or longer than 255 characters.
invalid_webhook_url400The webhook address is not HTTPS, not a URL, or points to a private or local server.
insufficient_balance402The balance is below the price.
below_api_minimum402Less than $20 on the balance — the API buys only from $20.
account_blocked403The account is blocked — write to support.
not_found404No order with this id for this key.
no_numbers409No numbers for this pair right now; nothing charged.
price_above_max409The price is above maxPrice; price holds the current one.
too_many_open_orders409Too many open orders — finish or cancel some.
invalid_state409Not possible in this status (e.g. finishing without a code).
too_early_to_cancel409Cancelling is possible from cancelableAt, 2 minutes after purchase.
idempotency_in_progress409A request with this Idempotency-Key is still running; repeat in a moment.
idempotency_key_reused422This Idempotency-Key was already used for a different request.
rate_limited429More than 20 requests a second; Retry-After says when to repeat.
buying_too_fast429More than 5 purchases a second; use /orders/batch.
try_later503Temporarily didn't work — try again in a minute.

REST examples

Buy a number, wait for the code, finish the order.

Python

import time, uuid, requests

BASE = "https://relysms.pro/api/v1"
S = requests.Session()
S.headers["Authorization"] = "Bearer rs_your_key"

r = S.post(f"{BASE}/orders",
           json={"service": "telegram", "country": "US", "maxPrice": 0.8},
           headers={"Idempotency-Key": str(uuid.uuid4())}, timeout=30)
if r.status_code != 201:
    raise SystemExit(r.json()["error"])   # no_numbers, below_api_minimum, …
order = r.json()["order"]
print("number:", order["phone"])

while order["status"] == "waiting":       # up to 20 minutes
    time.sleep(10)
    order = S.get(f"{BASE}/orders/{order['id']}", timeout=30).json()["order"]

if order["status"] == "received":
    print("code:", order["codes"][-1]["code"])
    S.post(f"{BASE}/orders/{order['id']}/finish", timeout=30)
# "expired" or "cancelled": the money is back on the balance

Node.js

const BASE = "https://relysms.pro/api/v1";
const headers = { Authorization: "Bearer rs_your_key", "Content-Type": "application/json" };

const res = await fetch(`${BASE}/orders`, {
  method: "POST",
  headers: { ...headers, "Idempotency-Key": crypto.randomUUID() },
  body: JSON.stringify({ service: "telegram", country: "US", maxPrice: 0.8 }),
});
if (res.status !== 201) throw new Error((await res.json()).error.code);
let { order } = await res.json();
console.log("number:", order.phone);

while (order.status === "waiting") {
  await new Promise((r) => setTimeout(r, 10_000));
  ({ order } = await (await fetch(`${BASE}/orders/${order.id}`, { headers })).json());
}
if (order.status === "received") {
  console.log("code:", order.codes.at(-1).code);
  await fetch(`${BASE}/orders/${order.id}/finish`, { method: "POST", headers });
}

Examples

The whole cycle: buy a number, wait for the code, finish the order.

curl

KEY=rs_your_key
BASE=https://relysms.pro/stubs/handler_api.php

curl "$BASE?api_key=$KEY&action=getBalance"
curl "$BASE?api_key=$KEY&action=getNumber&service=tg&country=187&maxPrice=0.80"
# → ACCESS_NUMBER:1042:15550001111
curl "$BASE?api_key=$KEY&action=getStatus&id=1042"
# → STATUS_WAIT_CODE … then STATUS_OK:48213
curl "$BASE?api_key=$KEY&action=setStatus&id=1042&status=6"

Python

import time, requests

BASE = "https://relysms.pro/stubs/handler_api.php"
KEY = "rs_your_key"

def call(action, **params):
    r = requests.get(BASE, params={"api_key": KEY, "action": action, **params}, timeout=30)
    r.raise_for_status()
    return r.text

answer = call("getNumber", service="tg", country="187", maxPrice="0.80")
if not answer.startswith("ACCESS_NUMBER"):
    raise SystemExit(answer)          # NO_NUMBERS, NO_BALANCE, …
_, order_id, phone = answer.split(":")
print("number:", phone)

for _ in range(120):                  # up to 20 minutes, every 10 s
    status = call("getStatus", id=order_id)
    if status.startswith("STATUS_OK:"):
        print("code:", status.split(":", 1)[1])
        call("setStatus", id=order_id, status=6)
        break
    if status == "STATUS_CANCEL":
        break                         # refunded automatically
    time.sleep(10)

Node.js

const BASE = "https://relysms.pro/stubs/handler_api.php";
const KEY = "rs_your_key";

async function call(action, params = {}) {
  const url = new URL(BASE);
  url.search = new URLSearchParams({ api_key: KEY, action, ...params });
  const res = await fetch(url);
  return res.text();
}

const answer = await call("getNumber", { service: "tg", country: "187", maxPrice: "0.80" });
if (!answer.startsWith("ACCESS_NUMBER")) throw new Error(answer);
const [, id, phone] = answer.split(":");
console.log("number:", phone);

for (let i = 0; i < 120; i++) {
  const status = await call("getStatus", { id });
  if (status.startsWith("STATUS_OK:")) {
    console.log("code:", status.slice("STATUS_OK:".length));
    await call("setStatus", { id, status: "6" });
    break;
  }
  if (status === "STATUS_CANCEL") break; // refunded automatically
  await new Promise((r) => setTimeout(r, 10_000));
}

Questions

My software used another activation service. What do I change?

The server address — to https://relysms.pro/stubs/handler_api.php; the key — to the one from your account. Service and country codes stay the same.

Why NO_BALANCE when I have money on the balance?

API purchases need a balance of $20 or more. The account and Mini App have no such floor.

What happens to the money if no code comes?

It returns to your balance on its own when the window closes, or at once if you cancel after 2 minutes.

Can I get a second code on the same number?

Yes, while the window is open: setStatus=3, then getStatus again.

Is there a test mode?

No: every purchase is real. Start with a cheap pair and check your flow on one number.

My key leaked. What now?

Account → Balance → API → Issue a new one. The old key stops working at once.

For AI assistants

The full docs as one text for ChatGPT, Claude and other models are at /developers.md, a short map at /llms.txt. Give the model the link and ask it to connect RelySMS to your code.

Read https://relysms.pro/developers.md and connect RelySMS to my script.

developers.md · llms.txt