Projelerinizden SMS ve tek kullanımlık doğrulama kodu göndermek için tek bir API. Alıcı GOLOS kullanıyorsa mesaj uygulamaya düşer ve paketinizden düşmez; kullanmıyorsa SMS olarak gider.
Bu API'yi kullanmak için Resco Telecom müşterisi olmanız yeterlidir. Her gönderim müşteri panelinizde görünür; ne gönderdiğinizi, hangi kanaldan gittiğini ve paketinizden ne düştüğünü her zaman izleyebilirsiniz.
Her istek HTTPS üzerinden gider ve dört başlıkla imzalanır. Gizli anahtar hiçbir istekte gönderilmez; yalnız imzayı üretmek için kullanılır. Sunucu aynı imzayı kendi tarafında hesaplar ve karşılaştırır.
| Başlık | Değer |
|---|---|
X-Resco-Anahtar | Anahtar kimliği, rt_<proje>_<8 hex> biçiminde (panelde görünür, gizli değildir). |
X-Resco-Zaman | Unix zamanı, saniye. Sunucu saatiyle fark en fazla ±300 saniye olabilir. |
X-Resco-Nonce | Her istekte yeni, 16–64 karakterlik rastgele dize (A-Za-z0-9_-). Aynı nonce ikinci kez kabul edilmez. |
X-Resco-Imza | Aşağıdaki metnin gizli anahtarla HMAC-SHA256 özeti, küçük harf hex. |
Content-Type | application/json |
Beş satır, aralarında \n (LF). Gövde özeti ham JSON'un SHA-256'sıdır; GET isteğinde gövde boş dizedir. Yol sorgu dizesi olmadan yazılır.
zaman
nonce
METHOD (POST veya GET)
/api/sms/gonder (yalnız yol, ?sorgu yok)
sha256hex(ham gövde)
imza = hex( HMAC-SHA256( gizli, metin ) )
/api/v1/… altında sürümlüdür; /api/… kısa yolu da aynı işi yapar ve en az 12 ay açık kalır. Yeni projede /api/v1/ kullanın; imzaladığınız yol çağırdığınız yolla aynı olmalıdır.imza_gecersiz döner.Serbest metinli SMS gönderir. Alıcı GOLOS kullanıcısıysa mesaj uygulamaya düşer (kanal: "golos") ve paketten düşmez; değilse SMS olarak gider ve parça sayısı kadar düşer.
{ "telefon": "0532 999 99 67", "metin": "Siparişiniz yola çıktı.", "gonderici": "ESQUIRE" } (gonderici isteğe bağlı)
200 → { "ok": true, "kanal": "sms", "parca": 1, "kodlama": "gsm7", "karakter": 22, "paket_kalan": 984 }
200 → { "ok": true, "kanal": "golos", "parca": 0, ... } (uygulamaya düştü, ücretsiz)
200 → { "ok": false, "error": "ulke_kapali", "engellendi": true } (SMS Geçidi kuralı)
402 → { "ok": false, "error": "paket_bitti", "parca": 2, "paket_kalan": 1 }
Telefon numarası ülke koduyla ya da 0532… biçiminde yazılabilir; sunucu düzeltir. Metin en fazla 1000 karakterdir.
Numaraya tek kullanımlık doğrulama kodu gönderir. Kod cevapta yer almaz; kullanıcı kodu size yazar, siz /api/otp/dogrula ile sorarsınız. Kod varsayılan olarak 6 hanelidir ve 3 dakika geçerlidir.
{ "telefon": "0532 999 99 67", "dil": "tr", "yontem": "sms", "sablon": "giris" } (yontem, sablon isteğe bağlı)
200 → { "ok": true, "kanal": "sms", "ttl": 180, "parca": 1, "paket_kalan": 983 }
200 → { "ok": true, "kanal": "golos", "ttl": 180, "parca": 0 }
429 → { "ok": false, "error": "sinir_asildi", "sinir": "numara_bekleme", "bekle_sn": 41 }
Kullanıcının yazdığı kodu doğrular. Kod bir kez doğrulanınca tükenir; yeni kod istenirse eskisi geçersiz olur.
{ "telefon": "0532 999 99 67", "kod": "482913" }
200 → { "ok": true }
401 → { "ok": false, "error": "kod_hatali", "kalan_deneme": 4 }
410 → { "ok": false, "error": "suresi_doldu" }
429 → { "ok": false, "error": "cok_deneme" }
Bir gönderimin teslim durumunu sorar. istek_no her cevabın kaydında ve müşteri panelindeki dökümde bulunur. Anlık bildirim için webhook kullanın; bu uç doğrulama ve geriye dönük sorgu içindir.
200 → { "ok": true, "istek_no": "…", "durum": "iletildi", "kanal": "sms", "parca": 1,
"gonderim_zamani": "2026-09-17T09:12:03.000Z", "teslim_zamani": "2026-09-17T09:12:09.000Z", "hata": null }
404 → { "ok": false, "error": "istek_yok" }
| durum | Anlamı |
|---|---|
gonderildi | Sağlayıcı kabul etti, teslim raporu henüz gelmedi. |
iletildi | Alıcının telefonuna ulaştı. |
iletilemedi | Şebeke teslim edemedi (kapalı telefon, geçersiz numara). |
golos | GOLOS uygulamasına düştü; anında teslim, paketten düşmedi. |
engellendi | SMS Geçidi kuralı gönderimi durdurdu (kapalı ülke gibi). |
hata | Sağlayıcı reddetti; hata alanında sebep. |
test | Test anahtarıyla yapıldı, gerçek gönderim yok. |
Her hata cevabı { "ok": false, "error": "<kod>" } biçimindedir. Kodlar sabittir; metne değil koda göre dallanın.
| HTTP | Kod | Anlamı ve ne yapmalı |
|---|---|---|
| 400 | https_gerekli | İstek HTTP ile geldi. Yalnız HTTPS kabul edilir. |
| 400 | telefon_gecersiz | Numara 10–15 rakama çözülemedi. |
| 400 | metin_gecersiz | Metin boş ya da 1000 karakterden uzun. |
| 400 | gonderici_gecersiz | İstekteki gonderici adı bu hesapta onaylı değil ya da yok. |
| 400 | yontem_gecersiz | yontem yalnız sms ya da arama olabilir. |
| 400 | sablon_yok | İstekteki sablon kodu bu hesapta tanımlı değil. |
| 400 | idempotency_gecersiz | Idempotency-Key 64 karakterden uzun ya da izinsiz karakter içeriyor. |
| 401 | anahtar_gecersiz | Anahtar kimliği tanınmadı ya da iptal edilmiş. |
| 401 | anahtar_askida | Anahtar askıda: anormallik askısı ya da şikâyet; sebep panelde görünür, müşteri hizmetleri açar. |
| 401 | imza_gecersiz | İmza tutmadı ya da nonce biçimi hatalı. İmzalanan metni ve gövde baytlarını kontrol edin. |
| 401 | zaman_asimi | Zaman farkı 300 saniyeyi aştı. Cevaptaki sunucu_zamani ile saatinizi düzeltin. |
| 401 | nonce_tekrar | Aynı nonce daha önce kullanıldı. Her istekte yeni nonce üretin. |
| 401 | kod_hatali | Doğrulama kodu yanlış; kalan_deneme kadar hak kaldı. |
| 402 | paket_bitti | SMS paketiniz bu mesajın parça sayısını karşılamıyor. Panelden paket alın. |
| 403 | kaynak_yasak | İstek, anahtarın IP listesi dışından geldi. |
| 404 | istek_yok | Böyle bir istek numarası bu anahtarda yok. |
| 409 | idempotency_isleniyor | Aynı Idempotency-Key ile ilk istek hâlâ sürüyor. Kısa süre sonra tekrar deneyin. |
| 410 | suresi_doldu | Kodun süresi geçti ya da yeni kod istendi. Yeniden kod isteyin. |
| 422 | icerik_reddedildi | Metin içerik politikasına takıldı: kara listedeki bir alan adı ya da bağlantıyla birlikte şifre/kart/kimlik isteyen kalıp. sebep ve eslesen alanları neyi söyler. |
| 422 | idempotency_cakisma | Aynı Idempotency-Key farklı bir gövdeyle kullanıldı. |
| 429 | sinir_asildi | Numara ya da proje sınırı aşıldı; sinir hangisi olduğunu, bekle_sn ne kadar bekleneceğini söyler. |
| 429 | cok_deneme | Kod için deneme hakkı bitti. Yeni kod isteyin. |
| 200 | <geçit sebebi> | Örn. ulke_kapali: istek geçerli ama SMS Geçidi kuralı gönderimi durdurdu; engellendi: true. Paketten düşmez. |
| 503 | servis_hazir_degil | Sunucu açılıyor; birkaç saniye sonra tekrar deneyin. |
Ağ kesilince aynı SMS'in iki kez gitmesini önlemek için isteğe Idempotency-Key başlığı ekleyin (en fazla 64 karakter, A-Za-z0-9_.:-). Aynı anahtar + aynı key 24 saat boyunca ilk cevabı aynen döndürür ve cevaba Idempotent-Replayed: true başlığı eklenir; ikinci gönderim olmaz, paketten düşmez.
Idempotency-Key: siparis-48812-kargo-bildirimi
SMS uzunluğu şebeke kuralıyla sayılır: yalnız GSM-7 karakterleri kullanan metin 160 karaktere kadar 1 parça, sonrası 153'er; Türkçe karakter (ç ğ ı ö ş ü İ) ya da emoji içeren metin UCS-2 sayılır, 70 karaktere kadar 1 parça, sonrası 67'şer. Her cevap parca, kodlama ve karakter döndürür. Paketten parça sayısı kadar düşer; GOLOS'a düşen mesaj 0 parçadır.
| Örnek metin | Kodlama | Parça |
|---|---|---|
| Siparisiniz yola cikti. (22 karakter) | gsm7 | 1 |
| Siparişiniz yola çıktı. (22 karakter) | ucs2 | 1 |
| Türkçe karakterli 120 karakterlik metin | ucs2 | 2 |
| Yalnız ASCII 320 karakterlik metin | gsm7 | 3 |
Panelde anahtar üretirken Test anahtarı kutusunu işaretleyin; kimliği rt_test_… ile başlar. Test anahtarıyla gelen istekler sağlayıcıya gitmez, paketten düşmez, sınır uygulanmaz. Sonuç numaranın son iki hanesine göre belirlenir; doğrulama kodu her zaman 000000'dır.
| Numara sonu | Sonuç |
|---|---|
…01 | Başarılı, teslim: "edildi" |
…02 | ok: false, error: "gonderilemedi" |
…03 | Gönderildi ama teslim: "edilmedi" |
| diğer | Başarılı |
Test istekleri de dökümde test sonucuyla görünür; canlıya geçerken yalnız anahtarı değiştirirsiniz.
Panelde anahtarınıza bir HTTPS webhook adresi tanımlarsanız her teslim olayı size anında POST edilir. Webhook gizlisi panelde bir kez gösterilir; gelen isteği bu gizliyle doğrulayın.
| Olay | Ne zaman |
|---|---|
sms.gonderildi | Sağlayıcı mesajı kabul etti. |
sms.iletildi | Teslim raporu geldi: telefona ulaştı. |
sms.iletilemedi | Teslim raporu geldi: ulaşmadı. |
sms.golos | Mesaj GOLOS uygulamasına düştü (anında). |
POST <webhook_url>
X-Resco-Zaman: 1758100323
X-Resco-Imza: hex( HMAC-SHA256( webhook_gizli, zaman + "\n" + ham gövde ) )
{ "olay": "sms.iletildi", "istek_no": "…", "telefon": "90532*****67", "kanal": "sms",
"durum": "iletildi", "teslim_zamani": "2026-09-17T09:12:09.000Z", "zaman": "2026-09-17T09:12:10.412Z" }
2xx dışı cevapta 30 saniye ve 5 dakika sonra iki kez daha denenir. Telefon numarası maskeli gelir. Her deneme panelinizde görünür.
engellendi döner.X-RateLimit-Limit (proje günlük tavanı) ve X-RateLimit-Remaining (bugün kalan) başlıkları döner.Telefonda görünen gönderici adı (örn. ESQUIRE) panelden talep edilir; sağlayıcı kaydı tamamlanınca “onaylı” olur. Onaylı adı ya projeye varsayılan olarak seçin ya da her istekte gonderici alanıyla gönderin. Onaysız ya da bilinmeyen ad 400 gonderici_gecersiz döner. Sağlayıcı o ülkede adı kabul etmezse mesaj varsayılan gönderici ile gider ve dökümde “gönderici düşüldü” görünür; GOLOS'a düşen mesaj onaylı gönderici adının kendi sohbetine düşer (örn. “ESQUIRE” sohbeti, bildirim başlığı ESQUIRE); onay yoksa “Resco” sistem sohbeti.
{ "telefon": "0532 999 99 67", "metin": "Siparişiniz yola çıktı.", "gonderici": "ESQUIRE" }
{ "telefon": "0532 999 99 67", "gonderici": "ESQUIRE" } (otp/iste)
Kural: 3–11 karakter, harf/rakam/boşluk, yalnız rakam olamaz. ABD ve Kanada alfanümerik gönderici adını desteklemez; bazı ülkelerde ön kayıt gerekir.
yontem: "arama" ile kod SMS yerine sesli aramayla okunur (sabit hatlar, SMS almayan numaralar); dil dil alanından (tr dahil). Sesli arama o anda yapılamazsa kod SMS olarak gider ve cevapta kanal gerçeği söyler: sms, arama ya da golos. Kod metnini kendiniz yazmak için panelde OTP şablonları bölümünde bir şablon (örn. giris) ve dil tanımlayın; istekte sablon ve dil verin. İstenen dil yoksa şablonun kaynak dili gider. Şablon panelden diğer dillere tek tıkla çevrilebilir.
{ "telefon": "+49 30 1234567", "yontem": "arama", "dil": "de" }
200 → { "ok": true, "kanal": "arama", "ttl": 120, "parca": 1 }
{ "telefon": "0532 999 99 67", "sablon": "giris", "dil": "de" }
200 → { "ok": true, "kanal": "sms", "ttl": 180, "parca": 1, "sablon": "giris", "dil": "de" }
400 → { "ok": false, "error": "sablon_yok" }
Şablon kuralı: {kod} zorunlu, {marka} (proje adı) ve {sure} (dakika) isteğe bağlı; en fazla 320 karakter. Sesli arama da paketten 1 birim düşer.
Anahtarınız çalınırsa zarar paketinizle sınırlıdır; ayrıca sistem anormallik askısı uygular: kısa sürede çok sayıda farklı numaraya istek, yüksek başarısızlık oranı ya da art arda içerik reddi anahtarı askıya alır (401 anahtar_askida, sebep ile). Askı panelde sebebiyle görünür; müşteri hizmetleri inceleyip açar. İçerik politikası: bağlantı içeren ve şifre/kart/kimlik isteyen metinler ile kara listedeki alan adları gönderilmez (422). İkinci doğrulama: panelde doğrulayıcı uygulaması bağlarsanız anahtar üretme/yenileme gibi işlemler SMS koduna ek uygulama kodu ister — SIM kopyalama tek başına yetmez; GOLOS kullanan hesaplarda SMS kodu zaten uygulamaya düşer. Her işlem denetim izine (kim, ne, ne zaman, hangi IP) yazılır.
422 → { "ok": false, "error": "icerik_reddedildi", "sebep": "oltalama", "eslesen": "Şifre isteyen metin + bağlantı" }
401 → { "ok": false, "error": "anahtar_askida", "sebep": "pompalama" }
Resco Telecom bu hizmeti Almanya · ABD · Estonya lisansları kapsamında ve lisanslı sağlayıcılar üzerinden sunar. Kişisel veriler GDPR'a uygun işlenir: müşteri veri sorumlusu, Resco Telecom veri işleyendir; işlenen veri alıcı numarası (maskeli), mesaj metni (geçici), teslim durumu ve istek kayıtlarıdır. API hizmet sözleşmesi ve veri işleme maddesi ilk proje anahtarı üretilirken panelde onaylanır; onay doğrulama koduyla birlikte kayda geçer (doğrulanmış numara, tarih, sürüm, IP). Metin: rescotelecom.com/sozlesme/sms-api · Fiyatlar: rescotelecom.com/fiyat
Üç örnek de aynı işi yapar: imzayı üretir ve /api/otp/iste çağırır. Anahtarları ortam değişkeninden okuyun; kodun içine yazmayın.
const crypto = require('crypto');
const ANAHTAR = process.env.RESCO_ANAHTAR, GIZLI = process.env.RESCO_GIZLI;
async function resco(yol, govdeObj) {
const govde = JSON.stringify(govdeObj);
const zaman = Math.floor(Date.now() / 1000);
const nonce = crypto.randomBytes(16).toString('hex');
const metin = [zaman, nonce, 'POST', yol, crypto.createHash('sha256').update(govde).digest('hex')].join('\n');
const imza = crypto.createHmac('sha256', GIZLI).update(metin).digest('hex');
const r = await fetch('https://rescotelecom.com' + yol, {
method: 'POST', body: govde,
headers: { 'Content-Type': 'application/json', 'X-Resco-Anahtar': ANAHTAR,
'X-Resco-Zaman': String(zaman), 'X-Resco-Nonce': nonce, 'X-Resco-Imza': imza }
});
return { status: r.status, json: await r.json() };
}
await resco('/api/otp/iste', { telefon: '0532 999 99 67' });
await resco('/api/v1/otp/dogrula', { telefon: '0532 999 99 67', kod: '482913' });
<?php
$anahtar = getenv('RESCO_ANAHTAR'); $gizli = getenv('RESCO_GIZLI');
function resco(string $yol, array $govdeObj): array {
global $anahtar, $gizli;
$govde = json_encode($govdeObj, JSON_UNESCAPED_UNICODE);
$zaman = time();
$nonce = bin2hex(random_bytes(16));
$metin = implode("\n", [$zaman, $nonce, 'POST', $yol, hash('sha256', $govde)]);
$imza = hash_hmac('sha256', $metin, $gizli);
$ch = curl_init('https://rescotelecom.com' . $yol);
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_POSTFIELDS => $govde, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', "X-Resco-Anahtar: $anahtar",
"X-Resco-Zaman: $zaman", "X-Resco-Nonce: $nonce", "X-Resco-Imza: $imza"],
]);
$cevap = curl_exec($ch); $kod = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
return ['status' => $kod, 'json' => json_decode($cevap, true)];
}
resco('/api/v1/otp/iste', ['telefon' => '0532 999 99 67']);
resco('/api/v1/otp/dogrula', ['telefon' => '0532 999 99 67', 'kod' => '482913']);
import os, time, json, hmac, hashlib, secrets, urllib.request
ANAHTAR, GIZLI = os.environ['RESCO_ANAHTAR'], os.environ['RESCO_GIZLI']
def resco(yol, govde_obj):
govde = json.dumps(govde_obj, ensure_ascii=False, separators=(',', ':')).encode()
zaman = str(int(time.time()))
nonce = secrets.token_hex(16)
metin = '\n'.join([zaman, nonce, 'POST', yol, hashlib.sha256(govde).hexdigest()])
imza = hmac.new(GIZLI.encode(), metin.encode(), hashlib.sha256).hexdigest()
istek = urllib.request.Request('https://rescotelecom.com' + yol, data=govde, method='POST', headers={
'Content-Type': 'application/json', 'X-Resco-Anahtar': ANAHTAR,
'X-Resco-Zaman': zaman, 'X-Resco-Nonce': nonce, 'X-Resco-Imza': imza})
try:
with urllib.request.urlopen(istek) as r: return r.status, json.load(r)
except urllib.error.HTTPError as e: return e.code, json.load(e)
resco('/api/v1/otp/iste', {'telefon': '0532 999 99 67'})
resco('/api/v1/otp/dogrula', {'telefon': '0532 999 99 67', 'kod': '482913'})
#!/bin/bash
YOL=/api/v1/otp/iste
GOVDE='{"telefon":"0532 999 99 67"}'
ZAMAN=$(date +%s)
NONCE=$(openssl rand -hex 16)
OZET=$(printf '%s' "$GOVDE" | openssl dgst -sha256 | awk '{print $NF}')
METIN=$(printf '%s\n%s\n%s\n%s\n%s' "$ZAMAN" "$NONCE" POST "$YOL" "$OZET")
IMZA=$(printf '%s' "$METIN" | openssl dgst -sha256 -hmac "$RESCO_GIZLI" | awk '{print $NF}')
curl -sS "https://rescotelecom.com$YOL" \
-H 'Content-Type: application/json' \
-H "X-Resco-Anahtar: $RESCO_ANAHTAR" -H "X-Resco-Zaman: $ZAMAN" \
-H "X-Resco-Nonce: $NONCE" -H "X-Resco-Imza: $IMZA" \
--data-raw "$GOVDE"
Resco Telecom · rescotelecom.com · destek@rescotelecom.com · Gizlilik politikası