Hızlı başlangıç
- 1. Bir anahtar oluşturun. Çalışma alanında API ve webhook'ları açın. Anahtarlar ilk bakiyenizi yükledikten sonra aktif olur; her anahtar yalnızca bir kez gösterilir, bu yüzden kopyalayın veya bir metin dosyası olarak kaydedin.
- 2. Maliyeti kontrol edin. Alıcıları, segmentleri ve toplam maliyeti göndermeden görmek için aynı gövdeyi
/costsadresine POST edin. - 3. Gönderin. Aynı gövdeyi
/sendsadresine POST edin. Yanıt, gönderi kimliğini içerir; mesajlar hemen veyasend_atzamanında gönderilir.
curl https://sendsmsonline.cc/api/v1/sends \
-H "Authorization: Bearer $SSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "recipients": [ { "to": "+447700900461", "fields": { "first_name": "Tom" } }, { "to": "+12015550142", "fields": { "first_name": "Ava" } } ], "text": "Hi {{first_name}}, your table is ready at 7:30.", "name": "Friday bookings", "client_ref": "bookings-1107" }'const res = await fetch("https://sendsmsonline.cc/api/v1/sends", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SSO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
recipients: [{ to: "+447700900461", fields: { first_name: "Tom" } }],
text: "Hi {{first_name}}, your table is ready at 7:30.",
}),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error.message);
console.log(data.send.id, data.send.cost_usd);import os, requests
r = requests.post(
"https://sendsmsonline.cc/api/v1/sends",
headers={"Authorization": f"Bearer {os.environ['SSO_API_KEY']}"},
json={
"recipients": [{"to": "+447700900461", "fields": {"first_name": "Tom"}}],
"text": "Hi {{first_name}}, your table is ready at 7:30.",
},
timeout=30,
)
data = r.json()
if r.status_code >= 400:
raise RuntimeError(data["error"]["message"])
print(data["send"]["id"], data["send"]["cost_usd"])<?php
$ch = curl_init("https://sendsmsonline.cc/api/v1/sends");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("SSO_API_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"recipients" => [["to" => "+447700900461", "fields" => ["first_name" => "Tom"]]],
"text" => "Hi {{first_name}}, your table is ready at 7:30.",
]),
]);
$data = json_decode(curl_exec($ch), true);
echo $data["send"]["id"] ?? $data["error"]["message"];Kimlik doğrulama
Anahtarınızı Authorization başlığında gönderin: Authorization: Bearer sso_live_.... Anahtarlar 49 karakterdir ve sso_live_ ile başlar. Hesap başına en fazla 5 anahtar; birini çalışma alanında iptal ederseniz hemen çalışmayı durdurur. Fiyat listesi herkese açıktır ve anahtar gerektirmez.
Hatalar
Hatalar, kararlı bir kod ve bir kişiye gösterebileceğiniz bir cümle içeren bir JSON gövdesi ile HTTP durum kodlarını kullanır. Bir istek başarısız olduğunda hiçbir ücret alınmaz.
HTTP/1.1 402 Payment Required
{ "error": { "code": "insufficient_credit", "message": "Short by $3.20. Add funds to send.", "total": "8.62480", "credit": "5.42000" } }| Alan | Tür | Anlam |
|---|---|---|
| 400 | invalid_json, text_missing, recipients_missing, too_many_recipients, too_many_segments, schedule_range, bad_send_at | İsteğin değiştirilmesi gerekiyor. |
| 401 | unauthorized | Eksik, yanlış veya iptal edilmiş anahtar. |
| 402 | insufficient_credit | Toplam için yeterli bakiye yok. |
| 404 | not_found | Böyle bir gönderi, mesaj, ülke veya uç nokta yok. |
| 409 | not_cancelable | Gönderim zaten başladı. |
| 429 | rate_limited | Bir dakikada 240'den fazla istek. |
Limitler
- Her anahtar için dakikada 240 istek, X-RateLimit-Limit, X-RateLimit-Remaining ve X-RateLimit-Reset'te gösterilir (unix saniyeleri).
- Her istek için 5,000 numara, her metin için 2,000 karakter, her mesaj için 10 segment.
- Mesajlar hesap başına dakikada en fazla 300 olarak gönderilir.
- send_at 10 dakikadan 30 güne kadar önceden.
Fiyatlar
GET/api/v1/prices
GET/api/v1/prices/{country}
Her ülke için mesaj başına fiyat (bir segment), 5 ondalık basamaklı USD olarak. Anahtar gerekmez.
{ "price": { "country": "GB", "name": "United Kingdom", "dial_code": "+44", "price_usd": "0.00868" }, "updated": "2026-11-07" }Hesap bakiyesi
GET/api/v1/account
{ "account": "SO8K2M4Q7X9TBA", "credit_usd": "41.27660", "currency": "USD" }Maliyet tahmini
POST/api/v1/costs
Gönderimle aynı gövdeyi alır ve göndermeden veya ücretlendirmeden ne kadar tutacağını döndürür. Her alıcının kendi birleştirilmiş metni segmentlerini belirler, böylece toplam kesindir.
{
"recipients": 2, "segments_min": 1, "segments_max": 1, "encoding": "gsm7", "messages_billed": 2,
"total_usd": "0.01426", "credit_usd": "41.27660", "enough_credit": true,
"countries": [ { "country": "GB", "recipients": 1, "price_usd": "0.00868", "subtotal_usd": "0.00868" },
{ "country": "US", "recipients": 1, "price_usd": "0.00558", "subtotal_usd": "0.00558" } ],
"dropped": [], "too_long": null
}Gönderi oluştur
POST/api/v1/sends
Bir veya birden fazla numaraya bir metin. Gönderim kabul edildiğinde tüm gönderim için kredi alınır; başarısız mesajlar tek tek iade edilir.
| Alan | Tür | Anlam |
|---|---|---|
| to | string or array | Uluslararası formatta numaralar (+447700900461). Bunu veya alıcıları kullanın. |
| recipients | array | Birleştirme alanları için to ve fields içeren nesneler: { "to": "+1...", "fields": { "first_name": "Ava" } }. |
| text | string | Mesaj, en fazla 2,000 karakter. {{field}} her alıcı için değiştirilir; eksik değerler boş olur. |
| send_at | string | Opsiyonel ISO 8601 zamanı offset veya Z ile. Olmadan gönderim hemen başlar. |
| name | string | Çalışma alanında gösterilen opsiyonel etiket (80 karakter). |
| client_ref | string | Webhook'larda döndürülen opsiyonel referans (64 karakter). |
| opt_out | boolean | Ayarlarda kayıtlı opt-out satırını metnin sonuna ekler. |
| default_country | string | Ülke kodu olmadan yazılan numaralar için kullanılan ISO kodu. |
HTTP/1.1 201 Created
{
"send": { "id": "CA7Q29XMAB4DFK", "status": "queued", "source": "api", "recipients": 2, "segments_max": 1,
"encoding": "gsm7", "cost_usd": "0.01426", "refunded_usd": "0.00000", "client_ref": "bookings-1107",
"send_at": null, "created_at": "2026-11-07T10:12:03.120Z", "started_at": null, "finished_at": null },
"dropped": [ { "value": "+4412", "code": "too_short", "message": "Too short" } ]
}Gönderilemeyen numaralar dropped içinde listelenir ve asla ücretlendirilmez. Kodlar: no_country_code, too_short, too_long, not_a_number, wrong_length, not_covered, duplicate ve suppressed (gönderme listesinde olmayanlar).
Gönderileri listele
GET/api/v1/sends/{id}
GET/api/v1/sends?limit=25&before={id}
Tek bir gönderim, mesaj durumuna göre counts içerir. Listeler en yeniden eskiye doğru sıralanır; bir sonraki sayfa için next_before'u before olarak geçirin. Çalışma alanından gönderimler de burada görünür, source: "web" ile.
Mesajlar
GET/api/v1/sends/{id}/messages?status=failed&limit=100
GET/api/v1/messages/{id}
{ "message": { "id": "MS3D8RW0K2QJ7N", "send_id": "CA7Q29XMAB4DFK", "to": "+447700900461", "country": "GB",
"segments": 1, "price_usd": "0.00868", "status": "delivered", "reason": null, "reason_text": null,
"created_at": "2026-11-07T10:12:03.120Z", "sent_at": "2026-11-07T10:12:04.010Z", "done_at": "2026-11-07T10:12:09.400Z" } }Gönderiyi iptal et
POST/api/v1/sends/{id}/cancel
Henüz başlamamış planlanmış gönderimler iptal edilebilir. Tam maliyet kredinize iade edilir ve cevap refunded_usd içerir.
Webhook'lar
Çalışma alanında bir https URL ayarlayın (API ve webhooks). Bir mesaj teslim edildiğinde veya başarısız olduğunda, API ve çalışma alanından gönderimler için bir etkinlik POST ederiz. 10 saniye içinde herhangi bir 2xx durumuyla cevap verin. Başarısız teslimatlar toplamda 7 kez tekrar denenir: hemen, ardından 1 dakika, 5 dakika, 30 dakika, 1 saat, 2 saat ve 3 saat sonra. Çalışma alanı her denemeyi durumu ve cevabıyla gösterir.
POST https://your-server.example/sms-events
X-SSO-Event: message.delivered
X-SSO-Delivery: evt_5a0c9e31d2b7f4a68c
X-SSO-Timestamp: 1794132729
X-SSO-Signature: sha256=8f1c...e42a
{ "id": "evt_5a0c9e31d2b7f4a68c", "event": "message.delivered", "created": "2026-11-07T10:12:09.000Z",
"data": { "message": { "id": "MS3D8RW0K2QJ7N", "send_id": "CA7Q29XMAB4DFK", "to": "+447700900461", "country": "GB",
"segments": 1, "price_usd": "0.00868", "status": "delivered", "reason": null, "reason_text": null,
"client_ref": "bookings-1107", "created_at": "...", "sent_at": "...", "done_at": "..." } } }İmza, imzalama sırrınızla timestamp + "." + raw body'nin HMAC-SHA256'sıdır (sso_whk_ ile başlar). Ayrıştırmadan önce ham gövde üzerinde kontrol edin, 5 dakikadan eski zaman damgalarını reddedin ve tekrarları göz ardı etmek için teslimat kimliğini kullanın.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (a string), before JSON.parse
export function verify(rawBody, headers, secret) {
const ts = headers["x-sso-timestamp"];
const sig = headers["x-sso-signature"] || "";
const want = "sha256=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
return fresh && sig.length === want.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(want));
}import hashlib, hmac, time
def verify(raw_body: bytes, headers, secret: str) -> bool:
ts = headers.get("X-SSO-Timestamp", "")
sig = headers.get("X-SSO-Signature", "")
mac = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256)
want = "sha256=" + mac.hexdigest()
fresh = ts.isdigit() and abs(time.time() - int(ts)) < 300
return fresh and hmac.compare_digest(sig, want)<?php
function verify(string $rawBody, string $ts, string $sig, string $secret): bool {
$want = "sha256=" . hash_hmac("sha256", $ts . "." . $rawBody, $secret);
return ctype_digit($ts) && abs(time() - (int)$ts) < 300 && hash_equals($want, $sig);
}Etkinlikler: message.delivered, message.failed ve Send test event düğmesinden test.
Durumlar ve nedenler
| Alan | Tür | Anlam |
|---|---|---|
| queued | status | Kuyruğumuzdan çıkmayı bekliyor. |
| sent | status | Ağa teslim edildi, teslimat raporu bekleniyor. Çoğu rapor dakikalar içinde gelir; bazı ağlar 24 saate kadar sürebilir. |
| delivered | status | Ağ, telefonun mesajı aldığını onayladı. |
| failed | status | Teslim edilmedi. Ücret bakiyenize iade edildi. |
| canceled | status | İptal ettiğiniz için gönderilmedi. İade edildi. |
Başarısızlık nedenleri (reason alanı):
| Alan | Tür | Anlam |
|---|---|---|
| network_error | reason | Ağ hatası |
| absent | reason | Telefon kapalı veya kapsama dışında |
| not_in_service | reason | Numara hizmet dışı |
| blocked | reason | Ağ veya alıcı tarafından engellendi |
| landline | reason | Sabit hat veya SMS alamayan numara |
| content_filter | reason | Ağın içerik filtresi tarafından reddedildi |
| expired | reason | Süresi doldu: ağın zaman sınırından önce rapor alınamadı |
| canceled | reason | Gönderilmeden iptal edildi |
| unknown | reason | Bilinmeyen ağ yanıtı |
Servis kontrolleri durum sayfasında ve API değişiklikleri güncelleme geçmişinde bulunur.