ওভারভিউ
আপনার নিজস্ব সিস্টেম থেকে বার্তা ট্রিগার হলে API ব্যবহার করুন: একটি দোকান থেকে অর্ডার আপডেট, লগইন ফর্ম থেকে কোড, বুকিং টুল থেকে রিমাইন্ডার। POST /v1/messages এ 10,000 পর্যন্ত নম্বর সহ একটি অনুরোধ একটি ক্যাম্পেইন তৈরি করে, ব্যালেন্স থেকে টাকা কাটে এবং প্রতিটি বার্তা কুইউতে রাখে। API দ্বারা তৈরি ক্যাম্পেইনগুলি তাদের ডেলিভারি রিপোর্ট সহ কনসোলেও দেখা যায়।
- সমস্ত পরিমাণ US ডলারে পাঁচ দশমিক সহ স্ট্রিং হিসাবে, উদাহরণস্বরূপ "0.00534"।
- ফোন নম্বরগুলি আন্তর্জাতিক, + চিহ্ন সহ বা ছাড়াই; স্পেস এবং ড্যাশ সরানো হয়।
- সময়গুলি UTC-তে ISO 8601 ফরম্যাটে, উদাহরণস্বরূপ 2026-12-01T09:00:00Z।
- বার্তা এবং ক্যাম্পেইন আইডিগুলি MSG-XXXX-XXXX-XXXX এবং CMP-XXXX-XXXX-XXXX এর মতো দেখায়।
অনুমোদন
কনসোলে API-এর অধীনে একটি API কী তৈরি করুন (আপনার প্রথম টপ-আপ যোগ করার পরে কীগুলি খোলে)। প্রতিটি অনুরোধের Authorization হেডারে এটি পাঠান। একটি কী bso_api_ এর পরে 36টি অক্ষর সহ দেখায়; এটি একবার দেখানো হয়, তাই এটিকে আপনার সার্ভারে একটি গোপন হিসাবে সংরক্ষণ করুন। একটি নতুন কী তৈরি করলে আগের কীটি সঙ্গে সঙ্গে প্রতিস্থাপিত হয়।
Authorization: Bearer bso_api_0123456789abcdef0123456789abcdef0123 Content-Type: application/json
সীমা
| অনুরোধ | প্রতি কীতে প্রতি মিনিটে 300; আরও বেশি হলে 429 rate_limited উত্তর আসে |
|---|---|
| প্রতি অনুরোধে নম্বর | 10,000 |
| বার্তার পাঠ্য | লিখিতভাবে 2,000 অক্ষর, সর্বোচ্চ 10 সেগমেন্ট |
| শিডিউলিং | send_at 5 মিনিট থেকে 60 দিন আগে পর্যন্ত |
| পাঠানোর গতি | প্রতি কুইউতে প্রতি মিনিটে 400টি বার্তা |
| ওয়েবহুক পুনরায় চেষ্টা | প্রায় 22 ঘন্টায় 9 বার চেষ্টা |
বার্তা পাঠান
POST/v1/messages
এক বা একাধিক নম্বরে একটি টেক্সট পাঠায়। অনুরোধ গ্রহণ করা হলে সম্পূর্ণ খরচ ব্যালেন্স থেকে কেটে নেওয়া হয়; প্রতিটি মেসেজ যা ডেলিভার করা যায় না তা পরে ফেরত দেওয়া হয়। উত্তরটি 201 স্ট্যাটাস এবং প্রতিটি মেসেজ সহ ক্যাম্পেইন প্রদান করে।
| ফিল্ড | টাইপ | বিবরণ |
|---|---|---|
toপ্রয়োজনীয় | string | string[] | একটি নম্বর বা আন্তর্জাতিক ফরম্যাটে 10,000 পর্যন্ত নম্বরের একটি অ্যারে। পুনরাবৃত্ত নম্বর একবার পাঠানো হয়; অপাঠযোগ্য নম্বরগুলি স্কিপ করা তালিকায় দেখানো হয়। |
textপ্রয়োজনীয় | string | মেসেজটি 2,000 অক্ষর এবং 10 সেগমেন্ট পর্যন্ত হতে পারে। GSM-7 টেক্সট প্রতি SMS-এ 160 অক্ষর ধারণ করে, ইউনিকোড 70। |
send_atঐচ্ছিক | string | পাঠানোর সময় নির্ধারণ করতে ISO 8601 সময়, 5 মিনিট থেকে 60 দিন আগে। এখনই পাঠাতে এটি বাদ দিন। |
opt_outঐচ্ছিক | boolean | true হলে কনসোল সেটিংস থেকে আপনার অপ্ট-আউট লাইন শেষে একটি নতুন লাইনে যোগ করে। |
client_refঐচ্ছিক | string | আপনার নিজের রেফারেন্স, 64টি অক্ষর, সংখ্যা এবং _ . : - পর্যন্ত। এটি রিপোর্ট এবং ওয়েবহুকগুলিতে ফেরত দেওয়া হয়। |
curl -X POST https://bulksmsonline.cc/api/v1/messages \
-H "Authorization: Bearer $BSO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to":["+447700900412","+33612345678"],"text":"Order 4521 has shipped. Track it at shop.example/t/4521","client_ref":"order-4521"}'const res = await fetch("https://bulksmsonline.cc/api/v1/messages", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.BSO_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ to: ["+447700900412", "+33612345678"], text: "Order 4521 has shipped.", client_ref: "order-4521" }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const campaign = await res.json();import os, requests
res = requests.post("https://bulksmsonline.cc/api/v1/messages",
headers={"Authorization": f"Bearer {os.environ['BSO_API_KEY']}"},
json={"to": ["+447700900412", "+33612345678"], "text": "Order 4521 has shipped.", "client_ref": "order-4521"},
timeout=30)
res.raise_for_status()
campaign = res.json()<?php
$ch = curl_init("https://bulksmsonline.cc/api/v1/messages");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BSO_API_KEY"), "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["to" => ["+447700900412", "+33612345678"], "text" => "Order 4521 has shipped.", "client_ref" => "order-4521"])]);
$campaign = json_decode(curl_exec($ch), true);HTTP/1.1 201 Created
{
"id": "CMP-7KQ2-M9XA-4HTD",
"status": "sending",
"channel": "api",
"recipients": 2,
"segments": 1,
"encoding": "GSM-7",
"cost": "0.02158",
"refunded": "0.00000",
"client_ref": "order-4521",
"send_at": null,
"counts": { "queued": 2, "submitted": 0, "delivered": 0, "failed": 0, "canceled": 0 },
"skipped": [],
"balance": "118.43120",
"messages": [
{ "id": "MSG-3RTW-81KD-ZQ0P", "to": "+447700900412", "country": "GB", "segments": 1, "price": "0.00834", "status": "queued" },
{ "id": "MSG-5HNA-4C2E-7YVB", "to": "+33612345678", "country": "FR", "segments": 1, "price": "0.01324", "status": "queued" }
]
}একটি পাঠানোর উদ্ধৃতি দিন
POST/v1/quote
/v1/messages-এর মতো একই বডি নেয় এবং পাঠানো বা চার্জ না করেই সঠিক খরচ, সেগমেন্ট এবং এনকোডিং ফেরত দেয়। আপনার ব্যবহারকারীদের নিশ্চিত করার আগে তাদের একটি মূল্য দেখাতে এটি ব্যবহার করুন।
{
"recipients": 2,
"skipped": [],
"segments": 1,
"encoding": "GSM-7",
"cost": "0.02158",
"balance": "118.45278",
"countries": [ { "country": "GB", "recipients": 1, "cost": "0.00834" }, { "country": "FR", "recipients": 1, "cost": "0.01324" } ]
}ব্যালেন্স
GET/v1/balance
{ "balance": "118.43120", "currency": "USD" }দর
GET/v1/rates
GET/v1/rates/{country}
প্রতিটি গন্তব্যের জন্য বা একটি দুই-অক্ষরের ISO দেশের কোডের জন্য একটি SMS সেগমেন্টের মূল্য। এই দুটি এন্ডপয়েন্ট পাবলিক এবং কোনও কী প্রয়োজন হয় না।
{ "country": "DE", "name": "Germany", "calling_code": "+49", "price": "0.01544", "currency": "USD", "per": "segment" }বার্তার অবস্থা
GET/v1/messages/{id}
{
"id": "MSG-3RTW-81KD-ZQ0P",
"campaign_id": "CMP-7KQ2-M9XA-4HTD",
"to": "+447700900412",
"country": "GB",
"segments": 1,
"price": "0.00834",
"status": "delivered",
"error": null,
"created_at": "2026-11-07T09:00:02Z",
"submitted_at": "2026-11-07T09:00:03Z",
"final_at": "2026-11-07T09:00:08Z"
}ক্যাম্পেইন
GET/v1/campaigns/{id}
GET/v1/campaigns/{id}/messages?offset=0&limit=100
প্রথমটি স্ট্যাটাস অনুযায়ী গণনা সহ ক্যাম্পেইন ফেরত দেয়; দ্বিতীয়টি এর মেসেজগুলির মাধ্যমে পেজ করে, প্রতি পৃষ্ঠায় 1,000 পর্যন্ত। কনসোল থেকে পাঠানো ক্যাম্পেইনগুলি তাদের পৃষ্ঠায় দেখানো id দিয়ে একইভাবে পড়া যেতে পারে।
একটি নির্ধারিত পাঠানো বাতিল করুন
POST/v1/campaigns/{id}/cancel
send_at দিয়ে তৈরি করা এবং এখনও শুরু হয়নি এমন একটি ক্যাম্পেইন বন্ধ করে। প্রতিটি মেসেজ বাতিল করা হয় এবং সম্পূর্ণ খরচ ব্যালেন্সে ফেরত আসে। একটি ক্যাম্পেইন যা ইতিমধ্যে পাঠাচ্ছে তা 409 not_scheduled উত্তর দেয়।
ডেলিভারি ওয়েবহুক
কনসোলে API-এর অধীনে একটি HTTPS URL যোগ করুন যাতে প্রতিটি চূড়ান্ত স্ট্যাটাস (ডেলিভার্ড বা ফেইল্ড) একটি POST অনুরোধ হিসাবে পাওয়া যায়। 8 সেকেন্ডের মধ্যে যেকোনো 2xx স্ট্যাটাস দিয়ে উত্তর দিন। অন্যান্য উত্তর এবং টাইমআউটগুলি 20 সেকেন্ড, 1, 5 এবং 15 মিনিট, তারপর 1, 3, 6 এবং 12 ঘন্টা পরে পুনরায় চেষ্টা করা হয়, মোট 9 বার চেষ্টা করা হয়। একই ইভেন্ট দুবার আসতে পারে, তাই এটি একবার প্রসেস করতে event_id ব্যবহার করুন।
POST https://your-server.example/sms-status
Content-Type: application/json
X-BulkSMSOnline-Signature: t=1794042008,v1=5f0c2a...e91b
{
"type": "message.status",
"event_id": "ev_8f1c22d0a4b97e63c1f0",
"sent_at": "2026-11-07T09:00:09Z",
"data": {
"message_id": "MSG-3RTW-81KD-ZQ0P",
"campaign_id": "CMP-7KQ2-M9XA-4HTD",
"to": "+447700900412",
"country": "GB",
"status": "delivered",
"error": null,
"segments": 1,
"price": "0.00834",
"currency": "USD",
"client_ref": "order-4521",
"final_at": "2026-11-07T09:00:08Z"
}
}স্বাক্ষর যাচাই করুন
হেডারে Unix সময় t এবং স্বাক্ষর v1 = HMAC-SHA256 of the string "t.raw_body" আপনার ওয়েবহুক সিক্রেট দিয়ে, হেক্সে থাকে। JSON পার্স করার আগে raw body-এর উপর এটি গণনা করুন, ধ্রুব সময়ে তুলনা করুন এবং পাঁচ মিনিটের পুরনো ইভেন্টগুলি প্রত্যাখ্যান করুন।
import crypto from "node:crypto";
export function verify(rawBody, header, secret) {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || "");
if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
const want = crypto.createHmac("sha256", secret).update(m[1] + "." + rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(m[2]));
}import hmac, hashlib, re, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
m = re.fullmatch(r"t=(\d+),v1=([0-9a-f]{64})", header or "")
if not m or abs(time.time() - int(m.group(1))) > 300:
return False
want = hmac.new(secret.encode(), m.group(1).encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(want, m.group(2))<?php
function verify(string $raw, string $header, string $secret): bool {
if (!preg_match('/^t=(\d+),v1=([0-9a-f]{64})$/', $header, $m)) return false;
if (abs(time() - (int)$m[1]) > 300) return false;
$want = hash_hmac('sha256', $m[1] . '.' . $raw, $secret);
return hash_equals($want, $m[2]);
}অবস্থা
| queued | গ্রহণ করা হয়েছে এবং কিউ থেকে বের হওয়ার জন্য অপেক্ষা করছে |
|---|---|
| submitted | গন্তব্য নেটওয়ার্কে হস্তান্তর করা হয়েছে |
| delivered | হ্যান্ডসেট দ্বারা নিশ্চিত করা হয়েছে (চূড়ান্ত) |
| failed | ডেলিভার করা যায়নি; মূল্য ফেরত দেওয়া হয়েছে (চূড়ান্ত) |
| canceled | পাঠানোর আগে একটি নির্ধারিত ক্যাম্পেইনের অংশ বাতিল করা হয়েছে (চূড়ান্ত) |
ক্যাম্পেইন স্ট্যাটাস হল scheduled, sending, complete বা canceled।
ত্রুটি
ত্রুটিগুলি একটি HTTP স্ট্যাটাস এবং একটি JSON বডি দিয়ে উত্তর দেয়: { "error": { "code": "insufficient_balance", "message": "..." } }। একটি অনুরোধ ত্রুটি দিয়ে উত্তর দিলে কোনও চার্জ করা হয় না।
| HTTP | কোড | অর্থ |
|---|---|---|
| 400 | invalid_request | একটি ফিল্ড অনুপস্থিত বা বৈধ নয়; বার্তাটি বলে কোনটি। |
| 400 | invalid_json | বডিটি JSON নয়। |
| 400 | no_valid_numbers | কোনো নম্বর পড়া যায়নি। |
| 401 | unauthorized | কীটি অনুপস্থিত, বিকৃত বা প্রতিস্থাপিত হয়েছে। |
| 402 | insufficient_balance | ব্যালেন্স খরচ কভার করছে না; টপ-আপ করে আবার পাঠান। |
| 404 | not_found | অজানা আইডি বা এন্ডপয়েন্ট। |
| 409 | not_scheduled | শুধুমাত্র একটি নির্ধারিত প্রচার যা শুরু হয়নি তা বাতিল করা যাবে। |
| 413 | too_many_recipients | একটি অনুরোধে 10,000 টির বেশি নম্বর। |
| 429 | rate_limited | এক মিনিটে 300 টির বেশি অনুরোধ। |
| 500 | server_error | অনুরোধটি পুনরায় চেষ্টা করুন; কিছু চার্জ করা হয়নি। |