Bắt đầu nhanh
- 1. Tạo một khóa. Trong không gian làm việc, mở API và webhooks. Khóa sẽ được mở sau khi bạn nạp tiền lần đầu tiên; mỗi khóa chỉ hiển thị một lần, vì vậy hãy sao chép hoặc lưu nó dưới dạng tệp văn bản.
- 2. Kiểm tra chi phí. POST cùng một nội dung đến
/costsđể xem người nhận, phân đoạn và tổng chi phí mà không cần gửi. - 3. Gửi. POST nó đến
/sends. Câu trả lời sẽ có id gửi; tin nhắn sẽ được gửi ngay lập tức hoặc tại thời điểmsend_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"];Xác thực
Gửi khóa của bạn trong tiêu đề Authorization: Authorization: Bearer sso_live_.... Khóa có 49 ký tự và bắt đầu bằng sso_live_. Tối đa 5 khóa mỗi tài khoản; thu hồi một khóa trong không gian làm việc và nó sẽ ngừng hoạt động ngay lập tức. Bảng giá là công khai và không cần khóa.
Lỗi
Lỗi sử dụng mã trạng thái HTTP và một phần thân JSON với mã ổn định và một câu bạn có thể hiển thị cho một người. Không có gì được tính phí khi một yêu cầu thất bại.
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" } }| Trường | Loại | Ý nghĩa |
|---|---|---|
| 400 | invalid_json, text_missing, recipients_missing, too_many_recipients, too_many_segments, schedule_range, bad_send_at | Yêu cầu cần thay đổi. |
| 401 | unauthorized | Thiếu, sai hoặc khóa đã bị thu hồi. |
| 402 | insufficient_credit | Không đủ số dư cho tổng chi phí. |
| 404 | not_found | Không có lần gửi, tin nhắn, quốc gia hoặc điểm cuối nào như vậy. |
| 409 | not_cancelable | Tin nhắn đã được gửi đi. |
| 429 | rate_limited | Hơn 240 yêu cầu trong một phút. |
Giới hạn
- 240 yêu cầu mỗi phút cho mỗi khóa, được hiển thị trong X-RateLimit-Limit, X-RateLimit-Remaining và X-RateLimit-Reset (giây unix).
- 5,000 số điện thoại mỗi yêu cầu, 2,000 ký tự mỗi tin nhắn, 10 phân đoạn mỗi tin nhắn.
- Tin nhắn được gửi đi với tốc độ tối đa 300 mỗi phút cho mỗi tài khoản.
- send_at từ 10 phút đến 30 ngày trước.
Giá
GET/api/v1/prices
GET/api/v1/prices/{country}
Giá mỗi tin nhắn (một phân đoạn) cho mỗi quốc gia, tính bằng USD với 5 chữ số thập phân. Không cần khóa.
{ "price": { "country": "GB", "name": "United Kingdom", "dial_code": "+44", "price_usd": "0.00868" }, "updated": "2026-11-07" }Số dư tài khoản
GET/api/v1/account
{ "account": "SO8K2M4Q7X9TBA", "credit_usd": "41.27660", "currency": "USD" }Ước tính chi phí
POST/api/v1/costs
Lấy cùng nội dung như một lần gửi và trả về chi phí dự kiến, mà không gửi hoặc tính phí. Văn bản hợp nhất của mỗi người nhận quyết định số phân đoạn, vì vậy tổng chi phí là chính xác.
{
"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
}Tạo một lần gửi
POST/api/v1/sends
Một tin nhắn đến một hoặc nhiều số điện thoại. Số dư cho toàn bộ lần gửi được trừ khi được chấp nhận; tin nhắn thất bại được hoàn lại từng cái một.
| Trường | Loại | Ý nghĩa |
|---|---|---|
| to | string or array | Số điện thoại ở định dạng quốc tế (+447700900461). Sử dụng cái này hoặc người nhận. |
| recipients | array | Đối tượng với to và fields, cho các trường hợp nhất: { "to": "+1...", "fields": { "first_name": "Ava" } }. |
| text | string | Tin nhắn, tối đa 2,000 ký tự. {{field}} được thay thế cho mỗi người nhận; giá trị thiếu sẽ trở thành trống. |
| send_at | string | Thời gian ISO 8601 tùy chọn với độ lệch hoặc Z. Nếu không có, tin nhắn sẽ được gửi ngay lập tức. |
| name | string | Nhãn tùy chọn hiển thị trong không gian làm việc (80 ký tự). |
| client_ref | string | Tham chiếu tùy chọn được trả về trong webhooks (64 ký tự). |
| opt_out | boolean | Thêm dòng từ chối nhận tin nhắn đã lưu trong Cài đặt vào cuối tin nhắn. |
| default_country | string | Mã ISO được sử dụng cho các số điện thoại không có mã quốc gia. |
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" } ]
}Các số điện thoại không thể gửi được liệt kê trong dropped và không bao giờ bị tính phí. Mã: no_country_code, too_short, too_long, not_a_number, wrong_length, not_covered, duplicate và suppressed (trong danh sách không gửi của bạn).
Lấy hoặc liệt kê các lần gửi
GET/api/v1/sends/{id}
GET/api/v1/sends?limit=25&before={id}
Một lần gửi bao gồm counts cho mỗi trạng thái tin nhắn. Danh sách được sắp xếp từ mới nhất trở đi; chuyển next_before thành before để xem trang tiếp theo. Các lần gửi từ không gian làm việc cũng xuất hiện ở đây, với source: "web".
Tin nhắn
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" } }Hủy một lần gửi
POST/api/v1/sends/{id}/cancel
Chỉ các lần gửi đã lên lịch nhưng chưa bắt đầu mới có thể hủy. Toàn bộ chi phí sẽ được hoàn lại vào số dư của bạn và câu trả lời bao gồm refunded_usd.
Webhooks
Đặt một URL https trong không gian làm việc (API và webhooks). Chúng tôi POST một sự kiện khi tin nhắn được gửi thành công hoặc thất bại, cho cả các lần gửi từ API và không gian làm việc. Trả lời với bất kỳ trạng thái 2xx nào trong vòng 10 giây. Các lần gửi thất bại sẽ được thử lại tối đa 7 lần: ngay lập tức, sau đó là 1 phút, 5 phút, 30 phút, 1 giờ, 2 giờ và 3 giờ. Không gian làm việc hiển thị mỗi lần thử với trạng thái và câu trả lời của nó.
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": "..." } } }Chữ ký là HMAC-SHA256 của timestamp + "." + raw body với khóa bí mật ký của bạn (bắt đầu bằng sso_whk_). Kiểm tra nó trên raw body trước khi phân tích, từ chối các timestamp cũ hơn 5 phút và sử dụng delivery id để bỏ qua các lần lặp lại.
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);
}Sự kiện: message.delivered, message.failed và test từ nút Gửi sự kiện thử nghiệm.
Trạng thái và lý do
| Trường | Loại | Ý nghĩa |
|---|---|---|
| queued | status | Đang chờ rời khỏi hàng đợi của chúng tôi. |
| sent | status | Đã gửi đến mạng, đang chờ báo cáo giao hàng. Hầu hết các báo cáo đến trong vài phút; một số mạng có thể mất đến 24 giờ. |
| delivered | status | Mạng đã xác nhận điện thoại nhận được tin nhắn. |
| failed | status | Không gửi được. Giá đã được hoàn lại vào số dư của bạn. |
| canceled | status | Không gửi vì bạn đã hủy. Đã hoàn tiền. |
Lý do thất bại (trường reason):
| Trường | Loại | Ý nghĩa |
|---|---|---|
| network_error | reason | Lỗi mạng |
| absent | reason | Điện thoại tắt hoặc ngoài vùng phủ sóng |
| not_in_service | reason | Số không còn hoạt động |
| blocked | reason | Bị chặn bởi mạng hoặc người nhận |
| landline | reason | Số cố định hoặc số không thể nhận SMS |
| content_filter | reason | Bị từ chối bởi bộ lọc nội dung của mạng |
| expired | reason | Hết hạn: không có báo cáo trước thời hạn của mạng |
| canceled | reason | Đã hủy trước khi gửi |
| unknown | reason | Phản hồi mạng không xác định |
Kiểm tra dịch vụ trên trạng thái trang và thay đổi API trong changelog.