Mabilisang pagsisimula
- 1. Gumawa ng key. Sa workspace, buksan ang API at webhooks. Magbubukas ang mga key pagkatapos ng unang paglalagay mo ng credit; isang beses lang ipapakita ang bawat key, kaya kopyahin o i-save ito bilang text file.
- 2. Tingnan ang halaga. I-POST ang parehong body sa
/costspara makita ang mga tatanggap, segmento at kabuuang halaga nang hindi nagpapadala. - 3. Magpadala. I-POST ito sa
/sends. Ang sagot ay may send id; aalis ang mga mensahe agad o sasend_at.
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"];Pagpapatunay
Ipadala ang iyong key sa Authorization header: Authorization: Bearer sso_live_.... Ang mga key ay 49 karakter at nagsisimula sa sso_live_. Hanggang 5 key bawat account; i-revoke ang isa sa workspace at titigil itong gumana agad. Ang listahan ng presyo ay pampubliko at hindi kailangan ng key.
Mga error
Gumagamit ang mga error ng HTTP status code at JSON body na may stable code at pangungusap na maaari mong ipakita sa isang tao. Walang sisingilin kapag nabigo ang isang request.
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" } }| Field | Uri | Kahulugan |
|---|---|---|
| 400 | invalid_json, text_missing, recipients_missing, too_many_recipients, too_many_segments, schedule_range, bad_send_at | Kailangan ng pagbabago ang request. |
| 401 | unauthorized | Nawawala, mali, o nirevoke na key. |
| 402 | insufficient_credit | Hindi sapat ang balance para sa kabuuan. |
| 404 | not_found | Walang ganitong pagpapadala, mensahe, bansa o endpoint. |
| 409 | not_cancelable | Nagsimula na ang pagpapadala. |
| 429 | rate_limited | Higit sa 240 kahilingan sa isang minuto. |
Mga limitasyon
- 240 kahilingan bawat minuto bawat key, ipinapakita sa X-RateLimit-Limit, X-RateLimit-Remaining at X-RateLimit-Reset (unix seconds).
- 5,000 numero bawat kahilingan, 2,000 karakter bawat text, 10 segment bawat mensahe.
- Umaalis ang mga mensahe hanggang 300 bawat minuto bawat account.
- send_at mula 10 minuto hanggang 30 araw sa hinaharap.
Mga presyo
GET/api/v1/prices
GET/api/v1/prices/{country}
Ang presyo bawat mensahe (isang segment) para sa bawat bansa, sa USD na may 5 decimal. Hindi kailangan ng key.
{ "price": { "country": "GB", "name": "United Kingdom", "dial_code": "+44", "price_usd": "0.00868" }, "updated": "2026-11-07" }Balance ng account
GET/api/v1/account
{ "account": "SO8K2M4Q7X9TBA", "credit_usd": "41.27660", "currency": "USD" }Tantiyahin ang halaga
POST/api/v1/costs
Kumukuha ng parehong body bilang isang pagpapadala at ibinabalik ang magiging gastos nito, nang hindi nagpapadala o nagchacharge. Ang sariling pinagsamang text ng bawat tatanggap ang nagdedecide sa mga segment nito, kaya eksakto ang kabuuan.
{
"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
}Gumawa ng pagpapadala
POST/api/v1/sends
Isang text sa isa o maraming numero. Kinukuha ang credit para sa buong pagpapadala kapag ito ay tinanggap; binabawi ang mga nabigong mensahe isa-isa.
| Field | Uri | Kahulugan |
|---|---|---|
| to | string or array | Mga numero sa international format (+447700900461). Gamitin ito o recipients. |
| recipients | array | Mga object na may to at fields, para sa merge fields: { "to": "+1...", "fields": { "first_name": "Ava" } }. |
| text | string | Ang mensahe, hanggang 2,000 karakter. {{field}} ay pinalitan bawat tatanggap; nawawalang mga value ay nagiging walang laman. |
| send_at | string | Opsyonal na ISO 8601 time na may offset o Z. Kung wala ito, magsisimula agad ang pagpapadala. |
| name | string | Opsyonal na label na ipinapakita sa workspace (80 karakter). |
| client_ref | string | Opsyonal na reference na ibinalik sa webhooks (64 karakter). |
| opt_out | boolean | Nagdadagdag ng opt-out line na nai-save sa Settings sa dulo ng text. |
| default_country | string | ISO code na ginagamit para sa mga numerong isinulat nang walang country code. |
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" } ]
}Ang mga numerong hindi maipapadala ay nakalista sa dropped at hindi kailanman sisingilin. Mga code: no_country_code, too_short, too_long, not_a_number, wrong_length, not_covered, duplicate at suppressed (sa iyong do-not-send list).
Kunin o ilista ang mga pagpapadala
GET/api/v1/sends/{id}
GET/api/v1/sends?limit=25&before={id}
Ang isang pagpapadala ay may kasamang counts bawat status ng mensahe. Ang mga listahan ay pinakabago muna; ipasa ang next_before bilang before para sa susunod na pahina. Ang mga pagpapadala mula sa workspace ay lumilitaw din dito, kasama ang source: "web".
Mga mensahe
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" } }Kanselahin ang pagpapadala
POST/api/v1/sends/{id}/cancel
Ang mga naka-schedule na pagpapadala na hindi pa nagsisimula ay maaaring kanselahin. Ang buong gastos ay ibabalik sa iyong credit at ang sagot ay may kasamang refunded_usd.
Mga webhook
Magtakda ng https URL sa workspace (API at webhooks). Nag-POST kami ng event kapag ang isang mensahe ay naihatid o nabigo, para sa mga pagpapadala mula sa API at workspace. Sumagot ng anumang 2xx status sa loob ng 10 segundo. Ang mga nabigong paghahatid ay susubukan muli hanggang 7 beses sa kabuuan: kaagad, pagkatapos ng 1 minuto, 5 minuto, 30 minuto, 1 oras, 2 oras at 3 oras. Ang workspace ay nagpapakita ng bawat pagtatangka kasama ang status at sagot nito.
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": "..." } } }Ang signature ay HMAC-SHA256 ng timestamp + "." + raw body gamit ang iyong signing secret (nagsisimula ito sa sso_whk_). Suriin ito sa raw body bago i-parse, tanggihan ang mga timestamp na mas matanda sa 5 minuto, at gamitin ang delivery id para balewalain ang mga ulit.
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);
}Mga event: message.delivered, message.failed, at test mula sa Send test event button.
Mga status at dahilan
| Field | Uri | Kahulugan |
|---|---|---|
| queued | status | Naghihintay na umalis sa aming pila. |
| sent | status | Naipasa na sa network, naghihintay ng delivery report. Karamihan ng reports ay dumarating sa loob ng ilang minuto; ang ibang network ay tumatagal hanggang 24 oras. |
| delivered | status | Kinumpirma ng network na natanggap ito ng phone. |
| failed | status | Hindi na-deliver. Ang halaga ay na-refund sa iyong credit. |
| canceled | status | Hindi naipadala dahil ikaw ang nag-cancel. Na-refund. |
Mga dahilan ng pagkabigo (ang reason field):
| Field | Uri | Kahulugan |
|---|---|---|
| network_error | reason | Error sa network |
| absent | reason | Naka-off ang phone o wala sa coverage |
| not_in_service | reason | Hindi aktibo ang numero |
| blocked | reason | Na-block ng network o ng recipient |
| landline | reason | Landline o numero na hindi tumatanggap ng SMS |
| content_filter | reason | Tinanggihan ng content filter ng network |
| expired | reason | Expired: walang report bago mag-expire ang time limit ng network |
| canceled | reason | Kinansela bago maipadala |
| unknown | reason | Hindi kilalang response ng network |
Ang service checks ay nasa status page at ang mga pagbabago sa API ay nasa changelog.