Base URL
All routes below use this base URL and return UTF-8 JSON.
https://master-sms.shop/api/v1
Master sms API
Integrate virtual numbers and SMS reception into your system with a secure and predictable API.
All routes below use this base URL and return UTF-8 JSON.
https://master-sms.shop/api/v1
Create a key in the dashboard, keep the secret server-side only, and make your first balance request.
Manage keysSend both credentials on every protected route.
Accept: application/json Content-Type: application/json X-API-Key: your_api_key X-API-Secret: your_api_secret
Success responses return success=true and data. Errors return success=false, error, and the corresponding HTTP status.
{
"success": true,
"data": {
"example": "value"
},
"timestamp": 1785078000
}{
"success": false,
"error": "Error description",
"code": 400
}The routes documented below match the public API v1 front controller.
/health
Public route
Confirms that the API is operational and returns its version.
{
"success": true,
"data": {
"version": "4.2.0",
"status": "operational",
"timestamp": 1785078000,
"server_time": "2026-07-26 12:00:00",
"request_id": "req_01K123EXAMPLE"
},
"timestamp": 1785078000
}/balance
Authentication required
Returns the account total, blocked and available balance, plus currency.
{
"success": true,
"data": {
"balance": 1250.5,
"blocked": 50,
"available": 1200.5,
"currency": "BRL",
"limit": 0
},
"timestamp": 1785078000
}/services
Authentication required
Lists countries, services, operators, providers, calculated prices, and stock.
| Parameter | Type | Description | Example |
|---|---|---|---|
country | string | Numeric country code. | 73 |
service | string | Service code, for example wa. | wa |
operator | string | Specific operator. It may also be a provider-supported list. | claro |
available | boolean | When true, returns only items in stock. | true |
provider | string | Provider name or identifier. | Hero SMS |
search | string | Text search by service. | WhatsApp |
GET https://master-sms.shop/api/v1/services?country=73&available=true
{
"success": true,
"data": [
{
"country_id": "73",
"country_name": "Brasil",
"services": [
{
"code": "wa",
"name": "WhatsApp",
"operator": "claro",
"price": 2.5,
"stock": 150,
"provider": "Hero SMS",
"category": "messenger",
"markup": 10
}
]
}
],
"timestamp": 1785078000
}/buy
Authentication required
Purchases a virtual number and creates an SMS activation.
| Field | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Service code, for example wa. |
country | string | Yes | Numeric country code. |
request_id | string | Yes | Unique purchase-attempt identifier used to prevent duplicates. |
operator | string | No | Specific operator. It may also be a provider-supported list. |
provider | string | No | Provider name or identifier. |
max_price | number | No | Maximum accepted price in the currency returned by the API. |
{
"service": "wa",
"country": "73",
"operator": "claro",
"provider": "Hero SMS",
"max_price": 5,
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"success": true,
"data": {
"activation_id": "ACT17850780001234",
"phone": "5511999999999",
"operator": "claro",
"price": 2.5,
"currency": "BRL",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"service_name": "WhatsApp"
},
"timestamp": 1785078000
}/status?activation_id={id}
Authentication required
Refreshes and returns an activation status, number, and SMS code.
| Parameter | Required | Description |
|---|---|---|
activation_id | Yes | ID returned by the purchase. |
| Status | Description |
|---|---|
pending | Waiting for SMS. |
received | SMS received; code contains the code. |
canceled | Activation canceled. |
expired | Activation expired. |
{
"success": true,
"data": {
"activation_id": "ACT17850780001234",
"status": "received",
"phone": "5511999999999",
"created_at": 1785078000,
"code": "123456",
"received_at": 1785078060
},
"timestamp": 1785078060
}/cancel
Authentication required
Cancels an eligible activation and reports the refund.
{ "activation_id": "ACT17850780001234" }{
"success": true,
"data": {
"activation_id": "ACT17850780001234",
"status": "canceled",
"refunded": true,
"refund_amount": 2.5
},
"timestamp": 1785078300
}/webhook
Authentication required
Registers, lists, and removes destinations for asynchronous events.
| Event | Description |
|---|---|
sms.received | SMS received. |
sms.purchased | Number purchased. |
sms.status | Activation status changed. |
balance.low | Balance below the configured threshold. |
| Field | Required | Description |
|---|---|---|
event | Yes | Signed event name. |
url | Yes | Public HTTPS URL that receives a JSON POST. |
secret | No | Secret used to create the HMAC SHA-256 signature. |
{
"event": "sms.received",
"url": "https://example.com/webhooks/master-sms",
"secret": "replace_with_a_random_secret"
}The list response never returns the stored secret.
{ "webhook_id": 1 }{
"event": "sms.received",
"timestamp": 1785078060,
"data": {
"activation_id": "ACT17850780001234",
"code": "123456",
"phone": "5511999999999",
"service": "wa",
"received_at": 1785078060
},
"signature": "legacy_body_signature"
}/stats
Authentication required
Returns the user totals for purchases, received messages, cancellations, spending, and refunds.
{
"success": true,
"data": {
"total_compras": 42,
"total_recebidos": 31,
"total_cancelados": 11,
"total_gasto": 79.5,
"total_estornado": 21
},
"timestamp": 1785078000
}/commissions
Authentication required
Returns pending and paid commissions plus the authenticated reseller history.
{
"success": true,
"data": {
"pending_total": 15.3,
"paid_total": 120,
"commissions": []
},
"timestamp": 1785078000
}The raw body is signed with HMAC SHA-256. Compare X-Webhook-Signature in constant time before processing JSON.
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $rawBody, getenv('MASTER_SMS_WEBHOOK_SECRET'));
if ($received === '' || !hash_equals($expected, $received)) {
http_response_code(401);
exit;
}
$event = json_decode($rawBody, true, flags: JSON_THROW_ON_ERROR);
http_response_code(204);
Minimal backend clients. Adapt timeouts, logging, and secret storage to your environment.
<?php
final class MasterSmsClient
{
public function __construct(
private string $apiKey,
private string $apiSecret,
private string $baseUrl = 'https://master-sms.shop/api/v1'
) {}
public function request(string $method, string $path, ?array $body = null): array
{
$ch = curl_init(rtrim($this->baseUrl, '/') . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/json',
'X-API-Key: ' . $this->apiKey,
'X-API-Secret: ' . $this->apiSecret,
],
CURLOPT_POSTFIELDS => $body === null ? null : json_encode($body),
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($raw === false) {
throw new RuntimeException(curl_error($ch));
}
curl_close($ch);
$result = json_decode($raw, true, flags: JSON_THROW_ON_ERROR);
if ($status < 200 || $status >= 300 || empty($result['success'])) {
throw new RuntimeException($result['error'] ?? "HTTP {$status}", $status);
}
return $result['data'] ?? [];
}
}
$client = new MasterSmsClient(
getenv('MASTER_SMS_API_KEY'),
getenv('MASTER_SMS_API_SECRET')
);
$balance = $client->request('GET', '/balance');
$services = $client->request('GET', '/services?country=73&available=true');
$purchase = $client->request('POST', '/buy', [
'service' => 'wa',
'country' => '73',
'request_id' => bin2hex(random_bytes(16)),
]);
import os
import uuid
import requests
class MasterSmsClient:
def __init__(self):
self.base_url = "https://master-sms.shop/api/v1"
self.session = requests.Session()
self.session.headers.update({
"Accept": "application/json",
"Content-Type": "application/json",
"X-API-Key": os.environ["MASTER_SMS_API_KEY"],
"X-API-Secret": os.environ["MASTER_SMS_API_SECRET"],
})
def request(self, method, path, *, params=None, json=None):
response = self.session.request(
method, self.base_url + path, params=params, json=json, timeout=30
)
payload = response.json()
if not response.ok or not payload.get("success"):
raise RuntimeError(payload.get("error", f"HTTP {response.status_code}"))
return payload.get("data", {})
client = MasterSmsClient()
balance = client.request("GET", "/balance")
services = client.request("GET", "/services", params={
"country": "73", "available": "true"
})
purchase = client.request("POST", "/buy", json={
"service": "wa",
"country": "73",
"request_id": str(uuid.uuid4()),
})
import crypto from "node:crypto";
const baseUrl = "https://master-sms.shop/api/v1";
const headers = {
Accept: "application/json",
"Content-Type": "application/json",
"X-API-Key": process.env.MASTER_SMS_API_KEY,
"X-API-Secret": process.env.MASTER_SMS_API_SECRET,
};
async function request(method, path, body) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30_000);
try {
const response = await fetch(baseUrl + path, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal,
});
const payload = await response.json();
if (!response.ok || !payload.success) {
throw new Error(payload.error ?? `HTTP ${response.status}`);
}
return payload.data ?? {};
} finally {
clearTimeout(timeout);
}
}
const balance = await request("GET", "/balance");
const purchase = await request("POST", "/buy", {
service: "wa",
country: "73",
request_id: crypto.randomUUID(),
});
export MASTER_SMS_API_KEY="your_api_key"
export MASTER_SMS_API_SECRET="your_api_secret"
export MASTER_SMS_BASE_URL="https://master-sms.shop/api/v1"
curl --fail-with-body --silent --show-error \
"$MASTER_SMS_BASE_URL/balance" \
-H "Accept: application/json" \
-H "X-API-Key: $MASTER_SMS_API_KEY" \
-H "X-API-Secret: $MASTER_SMS_API_SECRET"
curl --fail-with-body --silent --show-error \
"$MASTER_SMS_BASE_URL/buy" \
-X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-API-Key: $MASTER_SMS_API_KEY" \
-H "X-API-Secret: $MASTER_SMS_API_SECRET" \
--data '{"service":"wa","country":"73","request_id":"550e8400-e29b-41d4-a716-446655440000"}'
Copy this prompt, provide your stack, and send it to a coding AI. Replace only placeholders; never paste your API Secret into the conversation.
| Code | Meaning | Recommended handling |
|---|---|---|
400 | Bad request | Fix JSON, required fields, or operation rules. |
401 | Unauthenticated | Check X-API-Key and X-API-Secret. |
402 | Insufficient balance | Top up the account before purchasing. |
403 | Forbidden | Enable the key permission or check the IP whitelist. |
404 | Not found | Check the route, service, stock, or activation_id. |
405 | Invalid method | Use the documented HTTP method. |
409 | Idempotency conflict | Do not reuse request_id for another purchase. |
429 | Rate limit exceeded | Wait and retry with backoff. |
500 | Internal error | Record the request_id and retry without duplicating the purchase. |
The per-minute limit depends on the key and is returned in X-RateLimit-Limit. On HTTP 429, use exponential backoff with jitter.