ডেভেলপার
ডেভেলপারদের জন্য এসএমএস এপিআই
ছোট ও সহজবোধ্য REST এপিআই। cURL, Node.js, PHP, Python বা Go দিয়ে দুই মিনিটে প্রথম এসএমএস পাঠান।
দ্রুত শুরু
- 1
অ্যাকাউন্ট খুলুন
সাইন আপ করে মোবাইল নম্বর যাচাই করুন, ট্রায়াল ক্রেডিট পাবেন।
- 2
এপিআই কি তৈরি করুন
ড্যাশবোর্ডে API keys খুলে messages:send অনুমতিসহ একটি কি তৈরি করুন। এটি একবারই দেখানো হয়; BITSMS_API_KEY নামে সংরক্ষণ করুন।
- 3
মেসেজ পাঠান
নম্বর ও টেক্সটসহ POST /v1/messages করুন। 202 ও একটি মেসেজ আইডি পাবেন; এপিআই বা ওয়েবহুকে স্ট্যাটাস দেখুন।
এসএমএস পাঠানো
নম্বর 01XXXXXXXXX, 8801XXXXXXXXX বা +8801XXXXXXXXX যেকোনো ফরম্যাটে দিতে পারেন। ডিফল্ট নন-মাস্কিং সেন্ডার ব্যবহার করতে sender_id বাদ দিন।
curl https://sms-api.bitcommerz.com/v1/messages \
-H "Authorization: Bearer $BITSMS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-5521-shipped" \
-d '{"to": "01712345678", "text": "Your order #5521 has shipped."}'// Node.js 18+ (built-in fetch)
const res = await fetch("https://sms-api.bitcommerz.com/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BITSMS_API_KEY}`,
"Content-Type": "application/json",
// Same key on a retry = no duplicate SMS, no double charge.
"Idempotency-Key": "order-5521-shipped",
},
body: JSON.stringify({ to: "01712345678", text: "Your order #5521 has shipped." }),
});
const msg = await res.json();
console.log(res.status, msg.id, msg.status); // 202 "0192…" "queued"<?php
$ch = curl_init("https://sms-api.bitcommerz.com/v1/messages");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("BITSMS_API_KEY"),
"Content-Type: application/json",
"Idempotency-Key: order-5521-shipped",
],
CURLOPT_POSTFIELDS => json_encode([
"to" => "01712345678",
"text" => "আপনার অর্ডার #5521 পাঠানো হয়েছে।",
]),
]);
$body = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 202
echo $body["id"], " ", $body["parts"], " part(s)\n";import os, requests
res = requests.post(
"https://sms-api.bitcommerz.com/v1/messages",
headers={
"Authorization": f"Bearer {os.environ['BITSMS_API_KEY']}",
"Idempotency-Key": "order-5521-shipped",
},
json={"to": "01712345678", "text": "Your order #5521 has shipped."},
timeout=10,
)
msg = res.json()
print(res.status_code, msg["id"], msg["status"]) # 202 ... queuedbody, _ := json.Marshal(map[string]string{
"to": "01712345678",
"text": "Your order #5521 has shipped.",
})
req, _ := http.NewRequest("POST", "https://sms-api.bitcommerz.com/v1/messages", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("BITSMS_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "order-5521-shipped")
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err) // safe to retry with the same Idempotency-Key
}
defer res.Body.Close()
var msg struct{ ID, Status string }
json.NewDecoder(res.Body).Decode(&msg)
fmt.Println(res.StatusCode, msg.ID, msg.Status) // 202 ... queuedমেসেজের অবস্থা দেখা
স্ট্যাটাস শুধু সামনের দিকে যায়: queued, sent, তারপর delivered, failed বা expired। এগুলো জানার ভালো উপায় ওয়েবহুক।
curl https://sms-api.bitcommerz.com/v1/messages/$MESSAGE_ID \
-H "Authorization: Bearer $BITSMS_API_KEY"const res = await fetch(`https://sms-api.bitcommerz.com/v1/messages/${messageId}`, {
headers: { Authorization: `Bearer ${process.env.BITSMS_API_KEY}` },
});
const msg = await res.json();
// queued → sent → delivered | failed | expired
console.log(msg.status, msg.status_reason);<?php
$ch = curl_init("https://sms-api.bitcommerz.com/v1/messages/" . $messageId);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BITSMS_API_KEY")],
]);
$msg = json_decode(curl_exec($ch), true);
echo $msg["status"], "\n"; // queued, sent, delivered, failed, expiredres = requests.get(
f"https://sms-api.bitcommerz.com/v1/messages/{message_id}",
headers={"Authorization": f"Bearer {os.environ['BITSMS_API_KEY']}"},
timeout=10,
)
msg = res.json()
print(msg["status"], msg.get("status_reason"))req, _ := http.NewRequest("GET", "https://sms-api.bitcommerz.com/v1/messages/"+messageID, nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("BITSMS_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var msg struct {
Status string `json:"status"`
StatusReason *string `json:"status_reason"`
}
json.NewDecoder(res.Body).Decode(&msg)
fmt.Println(msg.Status)ওটিপি পাঠানো ও যাচাই
কোড BitSMS তৈরি করে এবং কখনো ফেরত দেয় না। যাচাইয়ে verified: true বা false আসে; ৫ বার ভুল কোড বা মেয়াদ শেষ হলে ওটিপি বন্ধ হয়ে যায়।
# 1. Send: BitSMS generates the code and never returns it.
curl https://sms-api.bitcommerz.com/v1/otp \
-H "Authorization: Bearer $BITSMS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: login-7f3a91" \
-d '{"to": "01712345678", "app_name": "MyShop"}'
# → 202 {"otp_id": "…", "message_id": "…", "expires_at": "…"}
# 2. Verify what the user typed.
curl https://sms-api.bitcommerz.com/v1/otp/$OTP_ID/verify \
-H "Authorization: Bearer $BITSMS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"code": "482913"}'
# → 200 {"verified": true}const headers = {
Authorization: `Bearer ${process.env.BITSMS_API_KEY}`,
"Content-Type": "application/json",
};
// 1. Send
const sent = await fetch("https://sms-api.bitcommerz.com/v1/otp", {
method: "POST",
headers: { ...headers, "Idempotency-Key": `login-${sessionId}` },
body: JSON.stringify({ to: "01712345678", app_name: "MyShop" }),
}).then((r) => r.json());
// 2. Verify
const result = await fetch(`https://sms-api.bitcommerz.com/v1/otp/${sent.otp_id}/verify`, {
method: "POST",
headers,
body: JSON.stringify({ code: userInput }),
}).then((r) => r.json());
if (result.verified) { /* sign the user in */ }<?php
function bitsms(string $path, array $body, array $extra = []): array {
$ch = curl_init("https://sms-api.bitcommerz.com/v1" . $path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array_merge([
"Authorization: Bearer " . getenv("BITSMS_API_KEY"),
"Content-Type: application/json",
], $extra),
CURLOPT_POSTFIELDS => json_encode($body),
]);
return json_decode(curl_exec($ch), true);
}
$otp = bitsms("/otp", ["to" => "01712345678", "app_name" => "MyShop"],
["Idempotency-Key: login-" . session_id()]);
$check = bitsms("/otp/" . $otp["otp_id"] . "/verify", ["code" => $_POST["code"]]);
if ($check["verified"] ?? false) { /* sign the user in */ }auth = {"Authorization": f"Bearer {os.environ['BITSMS_API_KEY']}"}
otp = requests.post(
"https://sms-api.bitcommerz.com/v1/otp",
headers={**auth, "Idempotency-Key": f"login-{session_id}"},
json={"to": "01712345678", "app_name": "MyShop"},
timeout=10,
).json()
check = requests.post(
f"https://sms-api.bitcommerz.com/v1/otp/{otp['otp_id']}/verify",
headers=auth,
json={"code": user_input},
timeout=10,
).json()
if check["verified"]:
... # sign the user intype otp struct {
OtpID string `json:"otp_id"`
}
var sent otp
post("/v1/otp", "login-"+sessionID, map[string]any{"to": "01712345678", "app_name": "MyShop"}, &sent)
var result struct {
Verified bool `json:"verified"`
AttemptsRemaining int `json:"attempts_remaining"`
}
post("/v1/otp/"+sent.OtpID+"/verify", "", map[string]any{"code": userInput}, &result)
// post marshals body, sets Authorization (and Idempotency-Key when non-empty),
// sends to https://sms-api.bitcommerz.com and decodes the JSON response into out.ওয়েবহুক সিগনেচার যাচাই
প্রতিটি ডেলিভারিতে BitSMS-Signature হেডার থাকে। ইভেন্টে ভরসা করার আগে আপনার এন্ডপয়েন্ট সিক্রেট দিয়ে র বডির সাথে মিলিয়ে নিন।
# Each delivery is signed:
# BitSMS-Signature: t=1730000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
# v1 = hex(HMAC-SHA256(webhook_secret, "<t>.<raw request body>"))
# Reject if t is more than 5 minutes old. During secret rotation there are
# two v1= values; accept either.import crypto from "node:crypto";
// Use the RAW body (e.g. express.raw({ type: "application/json" })).
export function verifyBitsms(rawBody, header, secret) {
const parts = header.split(",").map((p) => p.split("="));
const t = Number(parts.find(([k]) => k === "t")?.[1]);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
return parts
.filter(([k]) => k === "v1")
.some(([, sig]) => {
const got = Buffer.from(sig, "hex");
return got.length === expected.length && crypto.timingSafeEqual(got, expected);
});
}<?php
function verify_bitsms(string $rawBody, string $header, string $secret): bool {
$t = null; $sigs = [];
foreach (explode(",", $header) as $part) {
[$k, $v] = array_pad(explode("=", $part, 2), 2, "");
if ($k === "t") $t = (int) $v;
if ($k === "v1") $sigs[] = $v;
}
if (!$t || abs(time() - $t) > 300) return false;
$expected = hash_hmac("sha256", $t . "." . $rawBody, $secret);
foreach ($sigs as $sig) {
if (hash_equals($expected, $sig)) return true;
}
return false;
}
$ok = verify_bitsms(file_get_contents("php://input"),
$_SERVER["HTTP_BITSMS_SIGNATURE"] ?? "", getenv("BITSMS_WEBHOOK_SECRET"));import hashlib, hmac, time
def verify_bitsms(raw_body: bytes, header: str, secret: str) -> bool:
items = [p.split("=", 1) for p in header.split(",") if "=" in p]
t = next((int(v) for k, v in items if k == "t"), 0)
if not t or abs(time.time() - t) > 300:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(expected, v) for k, v in items if k == "v1")func VerifyBitsms(rawBody []byte, header, secret string) bool {
var t int64
var sigs []string
for _, part := range strings.Split(header, ",") {
k, v, _ := strings.Cut(part, "=")
switch k {
case "t":
t, _ = strconv.ParseInt(v, 10, 64)
case "v1":
sigs = append(sigs, v)
}
}
if t == 0 || math.Abs(float64(time.Now().Unix()-t)) > 300 {
return false
}
mac := hmac.New(sha256.New, []byte(secret))
fmt.Fprintf(mac, "%d.", t)
mac.Write(rawBody)
expected := mac.Sum(nil)
for _, s := range sigs {
got, err := hex.DecodeString(s)
if err == nil && hmac.Equal(got, expected) {
return true
}
}
return false
}এপিআই কীভাবে কাজ করে
অথেন্টিকেশন
কি পাঠান Authorization: Bearer bsk_live_… হিসেবে। প্রতিটি কি-র নির্দিষ্ট অনুমতি থাকে এবং আপনার সার্ভারের IP-তে সীমাবদ্ধ করা যায়। কি কখনো ব্রাউজার বা মোবাইল অ্যাপে রাখবেন না।
আইডেমপোটেন্সি
প্রতিটি সেন্ডে Idempotency-Key লাগে। একই কি ও বডি দিয়ে আবার চেষ্টা করলে আগের ফলাফলই ফেরত আসে, তাই নেটওয়ার্ক টাইমআউটে ডুপ্লিকেট এসএমএস বা দুবার চার্জ হয় না।
এরর
সব এররের গঠন এক: insufficient_credit বা invalid_destination-এর মতো স্থির কোড, পড়ার মতো বার্তা, আর সাপোর্টকে জানানোর জন্য একটি রিকোয়েস্ট আইডি।
রেট লিমিট
প্রতিটি কি-র নির্দিষ্ট রিকোয়েস্ট রেট আছে। রেসপন্সে X-RateLimit হেডার থাকে; সীমা ছাড়ালে Retry-After সহ 429 আসে।