# sms.tc API

sms.tc is a **multi-provider SMS / email verification number aggregator**. The
public API mirrors the SMS-Activate protocol so existing client libraries work
unmodified, while numbers are sourced from several upstream providers behind the
scenes. Because numbers come from different providers, some capabilities (**rent**
and **reactivate**) are **provider-dependent** and may not be available for every
number.

There are two API surfaces:

| Surface | Base URL | Auth | Format |
|---|---|---|---|
| REST | `https://sms.tc/api/v1` | header (`Authorization: ApiKey …`, `Bearer …`, or `X-Api-Key: …`) | JSON `{data, meta}` |
| SMS-Activate compat | `https://sms.tc/stubs/handler_api.php` | query (`?api_key=…`) | mostly plain text, JSON for V2/rent/email |

The full machine-readable contract is in [`openapi.yaml`](./openapi.yaml)
(OpenAPI 3.1).

---

## English

### 1. Get your API key

1. Log in to your account and open **Profile → API Key**.
2. Click **Generate** (or **Regenerate**). The plaintext key is shown **once** —
   copy it immediately. It is also emailed to you in your preferred language.
3. Keys look like `smstc_live_xxxxxxxxxxxxxxxxxxxxxxxx`. Only the prefix and the
   last-used timestamp are visible afterwards; the key itself is stored hashed
   (SHA-256) and cannot be recovered. Regenerating revokes the previous key.

> Your account email must be verified before an API key can be generated.

### 2. Authentication

**REST (`/api/v1`)** — send the key in a header. Any of these are accepted:

```
Authorization: ApiKey smstc_live_xxxxxxxxxxxx
Authorization: Bearer smstc_live_xxxxxxxxxxxx
X-Api-Key: smstc_live_xxxxxxxxxxxx
```

**SMS-Activate compat (`/stubs/handler_api.php`)** — send the key as the
`api_key` **query parameter** only (header auth is not used on this surface):

```
https://sms.tc/stubs/handler_api.php?api_key=smstc_live_xxxx&action=getBalance
```

### 3. Canonical codes (country id & service code)

The public `country` value is the **SMS-Activate canonical numeric country id**
(e.g. `6` = Indonesia, `0` = Russia, `62` = Turkey). The public `service` value
is the **SMS-Activate canonical 2–4 char short code** (e.g. `tg` = Telegram,
`wa` = WhatsApp, `ig` = Instagram, `go` = Google/Gmail).

These canonical values are mapped at the boundary to our internal catalog
(`sa_country_id` / `sa_service_code`). Use `getCountries` and `getServicesList`
to discover the exact ids/codes we currently carry — do not assume a static
table, since availability is synced from upstream providers. Codes or ids we
do not carry return `WRONG_SERVICE` / `WRONG_COUNTRY` (JSON) or `BAD_SERVICE` /
`NO_NUMBERS` (legacy plain text).

### 4. Currency

All prices use **ISO-4217 numeric currency codes**. The default and base
currency is **`840` (USD)**. Also accepted: `978` (EUR), `156` (CNY). Rent
actions additionally accept `643` (RUB). Internal USD prices are converted to
the requested currency at request time.

### 5. Quickstart — SMS-Activate compat

```bash
KEY=smstc_live_xxxxxxxxxxxx
BASE=https://sms.tc/stubs/handler_api.php

# Check balance -> ACCESS_BALANCE:100.5
curl "$BASE?api_key=$KEY&action=getBalance"

# Buy a Telegram number in Indonesia -> ACCESS_NUMBER:635468024:79584000000
curl "$BASE?api_key=$KEY&action=getNumber&service=tg&country=6"

# Poll for the code -> STATUS_WAIT_CODE ... then STATUS_OK:100001
curl "$BASE?api_key=$KEY&action=getStatus&id=635468024"

# Tell us the code was received and the service is activated (status 6)
#   -> ACCESS_ACTIVATION
curl "$BASE?api_key=$KEY&action=setStatus&id=635468024&status=6"
```

`setStatus` status values: **3** = request another code (`ACCESS_RETRY_GET`),
**6** = complete/confirm (`ACCESS_ACTIVATION`), **8** = cancel & refund
(`ACCESS_CANCEL`). Status **1** (number ready, `ACCESS_READY`) is accepted for
compatibility. Any other value returns `BAD_STATUS`.

`getStatus` returns one of: `STATUS_WAIT_CODE`, `STATUS_WAIT_RETRY:<code>`,
`STATUS_WAIT_RESEND`, `STATUS_OK:<code>`, `STATUS_CANCEL`.

### 6. Quickstart — REST `/api/v1`

```bash
KEY=smstc_live_xxxxxxxxxxxx
BASE=https://sms.tc/api/v1

# Browse offers (prices + availability) for Telegram & Google in Indonesia
curl "$BASE/activations/offers?services=tg,go&countries=6" \
  -H "Authorization: ApiKey $KEY"

# Purchase an activation
curl -X POST "$BASE/activations" \
  -H "Authorization: ApiKey $KEY" \
  -H "Content-Type: application/json" \
  -d '{"service":"tg","country":6}'

# Create an email activation
curl -X POST "$BASE/emails" \
  -H "Authorization: ApiKey $KEY" \
  -H "Content-Type: application/json" \
  -d '{"site":"telegram.com","domain":"gmail.com"}'
```

REST responses are JSON envelopes: `{ "data": ... }` (plus `{ "meta": ... }`
on batch endpoints).

### 7. Rent & reactivate (provider-dependent)

Rent (`getRentNumber`, `serviceCountRent`, `getRentServicesAndCountries`,
`prolong`, `prolongOptions`, `prolongHistory`) and reactivate (`reactivate`,
`reactivateOptions`) are only available when the upstream provider that owns the
number supports them. When unsupported, the API returns `ACTION_NOT_AVAILABLE`
(HTTP 400 JSON). `reactivate` and `prolong` are POST actions; parameters are
still passed in the query string. `reactivate` creates a **new** activation
linked to the original.

### 8. Error tokens

The legacy (SMS-Activate) surface emits **bare plain-text tokens** for classic
actions, and **JSON** `{ "title", "details", "info"? }` for V2/rent/email
actions. The REST surface always returns JSON envelopes
`{ "title", "details", ... }`.

| Token | HTTP (JSON) | Meaning |
|---|---|---|
| `NO_KEY` | 200 (stub) | `api_key` missing (stub returns 200 + token for compat) |
| `BAD_KEY` | 200 (stub) / 401 | API key invalid |
| `BAD_ACTION` | 404 | Unknown `action` |
| `BAD_SERVICE` / `WRONG_SERVICE` | 400 | Service unknown / unsupported |
| `WRONG_COUNTRY` | 400 | Country unknown |
| `WRONG_CURRENCY` | 400 | Unsupported currency |
| `BAD_STATUS` | 400 | Invalid `setStatus` value |
| `NO_NUMBERS` | 400/404 | No numbers available |
| `WRONG_MAX_PRICE` | 400 | `maxPrice` below minimum (`info.min`) |
| `WRONG_ACTIVATION_ID` | 400 | Activation id malformed |
| `NO_ACTIVATION` / `NOT_FOUND` | 404 | Activation does not exist |
| `NO_BALANCE` | 402 | Insufficient balance |
| `OFFER_NOT_FOUND` | 404 | No matching REST offer |
| `EARLY_CANCEL_DENIED` | 409 | Cannot cancel within first 2 min (`info.minActivationTime`) |
| `FREE_CANCELLATION_EXPIRED` | 409 | Cancel window (20 min) exceeded |
| `OTP_RECEIVED` / `NEW_OTP_RECEIVED` | 409 | Code already received |
| `ACTIVATION_NOT_ACTIVE` | 409 | Activation terminated/refunded |
| `BAD_DURATION` | 400 | Rent/prolong duration invalid (`info.min/max`) |
| `MAX_HOURS_EXCEED` | 400 | Prolong exceeds max hours (`info.max`) |
| `SIM_OFFLINE` / `SIM_TEMPORARY_OFFLINE` | 403 | Number temporarily unavailable |
| `SERVICE_NOT_AVAILABLE` | 403 | Service not available for sale |
| `ACTION_NOT_AVAILABLE` | 400 | Action unsupported by this provider (rent/reactivate) |
| `CHANNELS_LIMIT` | 403 | Concurrent-thread limit reached |
| `ACCOUNT_INACTIVE` | 403 | Account not activated |
| `BANNED` | 403 | Account temporarily suspended (`info.scope/banned_until`) |
| `RATE_LIMIT` | 429 | Too many requests (`Retry-After` header) |
| `OPERATORS_NOT_FOUND` | — | `getOperators`: none found |
| `SERVER_ERROR` | 500 | Internal error |

### 9. Rate limits

Requests are throttled per API key. Exceeding a limit returns HTTP `429` with a
`RATE_LIMIT` body and a `Retry-After` header (seconds). Number-purchase actions
have a tighter limit than read actions.

### 10. SMS success rate (`rate`)

Offer and catalog responses may include a `rate` field: the share of **our own
settled activations** that successfully received an SMS, as a percentage
(0–100, one decimal) or `null`.

- **Scope:** computed per **(provider + service + country)**. A country's rate
  is never derived from another country's activations.
- **Counting:** `completed` = success, `timeout` / `cancelled` / `banned` =
  failure. `pending` activations are excluded. The rates upstream providers
  report about themselves are **never** used.
- **Minimum sample:** the rate is only reported when that exact combination has
  at least **10** settled activations — otherwise it is `null`.
  `GET /activations/offers/providers` echoes the threshold as
  `meta.min_rate_sample`.
- **Freshness:** recomputed **once a day** and cached; it is a rolling metric,
  not a live per-request query.

Where it appears:

| Endpoint | Field | Meaning |
|---|---|---|
| `GET /activations/offers` | `data.<service>.<country>.rate` | Rate of the provider behind the returned `prices.default` (the offer that would be served) |
| `GET /activations/offers/providers` | `data.providers[].rate` | Rate of that specific provider/operator |
| `GET /catalog/prices` | `data.<country>.<service>.rate` | Rate of the provider behind the returned `cost` |
| stub `action=getPrices` | `rate` (non-standard) | Same as above; ignore it if you strictly validate the SMS-Activate shape |

`rate` is `null` — never omitted — when there is not enough data, so you can
always tell "not enough data" apart from "field missing".

---

## Türkçe

### 1. API anahtarınızı alın

1. Hesabınıza giriş yapın ve **Profil → API Anahtarı** sayfasını açın.
2. **Oluştur** (veya **Yeniden Oluştur**) düğmesine tıklayın. Düz metin anahtar
   yalnızca **bir kez** gösterilir — hemen kopyalayın. Tercih ettiğiniz dilde
   e-posta ile de gönderilir.
3. Anahtarlar `smstc_live_xxxxxxxxxxxxxxxxxxxxxxxx` biçimindedir. Sonrasında
   yalnızca önek ve son kullanım zamanı görünür; anahtar SHA-256 ile hash'lenmiş
   olarak saklanır ve geri alınamaz. Yeniden oluşturma önceki anahtarı iptal
   eder.

> API anahtarı oluşturabilmek için hesap e-postanızın doğrulanmış olması gerekir.

### 2. Kimlik doğrulama

**REST (`/api/v1`)** — anahtarı başlık (header) ile gönderin. Aşağıdakilerin
herhangi biri kabul edilir:

```
Authorization: ApiKey smstc_live_xxxxxxxxxxxx
Authorization: Bearer smstc_live_xxxxxxxxxxxx
X-Api-Key: smstc_live_xxxxxxxxxxxx
```

**SMS-Activate uyumlu (`/stubs/handler_api.php`)** — anahtarı yalnızca `api_key`
**sorgu parametresi** olarak gönderin (bu yüzeyde başlık kimlik doğrulaması
kullanılmaz):

```
https://sms.tc/stubs/handler_api.php?api_key=smstc_live_xxxx&action=getBalance
```

### 3. Kanonik kodlar (ülke id'si ve servis kodu)

Genel `country` değeri **SMS-Activate kanonik sayısal ülke id'sidir** (örn.
`6` = Endonezya, `0` = Rusya, `62` = Türkiye). Genel `service` değeri ise
**SMS-Activate kanonik 2–4 karakterlik kısa kodudur** (örn. `tg` = Telegram,
`wa` = WhatsApp, `ig` = Instagram, `go` = Google/Gmail).

Bu kanonik değerler, sınırda iç kataloğumuza (`sa_country_id` / `sa_service_code`)
eşlenir. Şu an taşıdığımız kesin id/kodları keşfetmek için `getCountries` ve
`getServicesList` kullanın — sağlayıcılardan senkronize edildiği için sabit bir
tablo varsaymayın. Taşımadığımız kod veya id'ler `WRONG_SERVICE` / `WRONG_COUNTRY`
(JSON) ya da `BAD_SERVICE` / `NO_NUMBERS` (eski düz metin) döndürür.

### 4. Para birimi

Tüm fiyatlar **ISO-4217 sayısal para birimi kodları** kullanır. Varsayılan ve
temel para birimi **`840` (USD)**'dir. Ayrıca kabul edilenler: `978` (EUR),
`156` (CNY). Kiralama (rent) işlemleri ek olarak `643` (RUB) kabul eder.
Dahili USD fiyatları istek anında talep edilen para birimine çevrilir.

### 5. Hızlı başlangıç — SMS-Activate uyumlu

```bash
KEY=smstc_live_xxxxxxxxxxxx
BASE=https://sms.tc/stubs/handler_api.php

# Bakiye -> ACCESS_BALANCE:100.5
curl "$BASE?api_key=$KEY&action=getBalance"

# Endonezya'da Telegram numarası al -> ACCESS_NUMBER:635468024:79584000000
curl "$BASE?api_key=$KEY&action=getNumber&service=tg&country=6"

# Kodu bekle -> STATUS_WAIT_CODE ... ardından STATUS_OK:100001
curl "$BASE?api_key=$KEY&action=getStatus&id=635468024"

# Kodun alındığını ve servisin etkinleştirildiğini bildir (status 6)
#   -> ACCESS_ACTIVATION
curl "$BASE?api_key=$KEY&action=setStatus&id=635468024&status=6"
```

`setStatus` değerleri: **3** = yeni kod iste (`ACCESS_RETRY_GET`), **6** =
tamamla/onayla (`ACCESS_ACTIVATION`), **8** = iptal et ve iade al
(`ACCESS_CANCEL`). **1** değeri (numara hazır, `ACCESS_READY`) uyumluluk için
kabul edilir. Diğer değerler `BAD_STATUS` döndürür.

`getStatus` şunlardan birini döndürür: `STATUS_WAIT_CODE`,
`STATUS_WAIT_RETRY:<kod>`, `STATUS_WAIT_RESEND`, `STATUS_OK:<kod>`,
`STATUS_CANCEL`.

### 6. Hızlı başlangıç — REST `/api/v1`

```bash
KEY=smstc_live_xxxxxxxxxxxx
BASE=https://sms.tc/api/v1

# Teklifleri görüntüle (fiyat + stok)
curl "$BASE/activations/offers?services=tg,go&countries=6" \
  -H "Authorization: ApiKey $KEY"

# Aktivasyon satın al
curl -X POST "$BASE/activations" \
  -H "Authorization: ApiKey $KEY" \
  -H "Content-Type: application/json" \
  -d '{"service":"tg","country":6}'

# E-posta aktivasyonu oluştur
curl -X POST "$BASE/emails" \
  -H "Authorization: ApiKey $KEY" \
  -H "Content-Type: application/json" \
  -d '{"site":"telegram.com","domain":"gmail.com"}'
```

REST yanıtları JSON zarflarıdır: `{ "data": ... }` (toplu uç noktalarda ayrıca
`{ "meta": ... }`).

### 7. Kiralama ve yeniden aktivasyon (sağlayıcıya bağlı)

Kiralama (`getRentNumber`, `serviceCountRent`, `getRentServicesAndCountries`,
`prolong`, `prolongOptions`, `prolongHistory`) ve yeniden aktivasyon
(`reactivate`, `reactivateOptions`), yalnızca numarayı sağlayan üst sağlayıcının
bu özelliği desteklediği durumlarda kullanılabilir. Desteklenmediğinde API
`ACTION_NOT_AVAILABLE` (HTTP 400 JSON) döndürür. `reactivate` ve `prolong` POST
işlemleridir; parametreler yine sorgu dizesinde geçirilir. `reactivate`,
orijinaline bağlı **yeni** bir aktivasyon oluşturur.

### 8. Hata kodları (token)

Eski (SMS-Activate) yüzeyi klasik işlemler için **çıplak düz metin token**,
V2/kiralama/e-posta işlemleri için **JSON** `{ "title", "details", "info"? }`
döndürür. REST yüzeyi her zaman JSON zarfı `{ "title", "details", ... }` döner.
Token tablosu için yukarıdaki İngilizce **Error tokens** bölümüne bakın.

### 9. Hız limitleri

İstekler API anahtarı başına sınırlandırılır. Limit aşıldığında HTTP `429`,
`RATE_LIMIT` gövdesi ve `Retry-After` başlığı (saniye) döner. Numara satın alma
işlemleri okuma işlemlerinden daha düşük bir limite sahiptir.

### 10. SMS gelme oranı (`rate`)

Teklif ve katalog yanıtları bir `rate` alanı içerebilir: **kendi sonuçlanmış
aktivasyonlarımızın** SMS alma oranı; yüzde olarak (0–100, tek ondalık) ya da
`null`.

- **Kapsam:** **(sağlayıcı + servis + ülke)** bazında hesaplanır. Bir ülkenin
  oranı asla başka bir ülkenin aktivasyonlarından türetilmez.
- **Sayım:** `completed` = başarılı, `timeout` / `cancelled` / `banned` =
  başarısız. `pending` aktivasyonlar hesaba katılmaz. Sağlayıcıların kendi
  hakkında bildirdiği oranlar **hiçbir zaman** kullanılmaz.
- **Minimum örneklem:** oran yalnızca ilgili kombinasyonda en az **10**
  sonuçlanmış aktivasyon varsa döner; aksi halde `null` olur.
  `GET /activations/offers/providers` yanıtı eşiği `meta.min_rate_sample`
  olarak bildirir.
- **Tazelik:** **günde bir kez** hesaplanıp önbelleğe alınır; istek anında
  canlı sorgu değildir.

Nerede görünür:

| Uç nokta | Alan | Anlamı |
|---|---|---|
| `GET /activations/offers` | `data.<servis>.<ülke>.rate` | Dönen `prices.default` fiyatının arkasındaki sağlayıcının oranı (satın alma anında seçilecek teklif) |
| `GET /activations/offers/providers` | `data.providers[].rate` | O sağlayıcı/operatöre ait oran |
| `GET /catalog/prices` | `data.<ülke>.<servis>.rate` | Dönen `cost` fiyatının arkasındaki sağlayıcının oranı |
| stub `action=getPrices` | `rate` (standart dışı) | Yukarıdakiyle aynı; SMS-Activate biçimini katı doğruluyorsanız yok sayın |

Yeterli veri yoksa `rate` alanı **atlanmaz**, `null` döner; böylece "veri yok"
ile "alan yok" her zaman ayırt edilebilir.

---

> Note: This is a public, stateless API. The `/api/v1` and `/stubs/*` endpoints
> never use web sessions or CSRF; authenticate every request with your API key.
