sent হয়ে ফিরে আসে
ইমেইল রিকোয়েস্টের সময়েই পাঠিয়ে দেওয়া হয়। 201 রেসপন্সেই status "sent" এবং একটি sentAt টাইমস্ট্যাম্প থাকে, ফলে সিনক্রোনাস কলারের কাজ সেখানেই শেষ।
POST /api/v1/messages ইমেইল, এসএমএস বা হোয়াটসঅ্যাপ পাঠায়। তেরোটি এন্ডপয়েন্ট মেসেজ, কন্টাক্ট, টেমপ্লেট ও অ্যানালিটিক্স কভার করে। স্কোপ করা কী, নিরাপদ রিট্রাই, সাইন করা ডেলিভারি ওয়েবহুক, আর চালু করে নেওয়ার মতো একটি প্রকাশিত এরর ক্যাটালগ।
curl -X POST https://server.quickgrow.ai/api/v1/messages \ -H "X-API-Key: mk_..." \ -H "Idempotency-Key: order-1042-shipped" \ -H "Content-Type: application/json" \ -d '{ "channel": "sms", "to": "+8801700000000", "text": "Order 1042 has shipped.", "tags": ["order-shipped"], "metadata": { "orderId": "1042" } }' # 201 Created # { "data": { "id": "msg_9f8a3bc2e41d", "status": "queued" } }
বডির গঠন বদলায় চ্যানেল অনুযায়ী, এন্ডপয়েন্ট অনুযায়ী নয়। ইনলাইন কনটেন্ট পাঠান, অথবা id দিয়ে সংরক্ষিত টেমপ্লেট রেফার করে তার ভেরিয়েবলগুলো দিন। আপনার নিজের ট্যাগ ও একটি metadata অবজেক্ট সঙ্গে যায়, এবং প্রতিটি স্ট্যাটাস রিড ও প্রতিটি ওয়েবহুকে ফিরে আসে।
{
"channel": "email",
"to": "[email protected]",
"subject": "Your invoice",
"html": "<p>{tr(locale, "Invoice INV-1042 is attached.")}</p>",
"fromName": "Rahman Traders",
"replyTo": "[email protected]",
"attachments": [
{ "filename": "invoice.pdf",
"contentType": "application/pdf",
"content": "JVBERi0xLjQK..." }
]
}{
"channel": "whatsapp",
"to": "+8801700000000",
"templateId": "tpl_7c31a9d4b2e6",
"variables": { "customerName": "Alex", "orderId": "1042" }
}
# templateId, not a template name.
# The template channel must match "channel".{
"data": {
"id": "msg_9f8a3bc2e41d",
"channel": "email",
"status": "delivered",
"to": "[email protected]",
"tags": ["order-confirmation"],
"metadata": { "orderId": "1042" },
"createdAt": "2026-08-10T09:14:22.000Z",
"sentAt": "2026-08-10T09:14:22.000Z",
"deliveredAt": "2026-08-10T09:14:25.000Z",
"failedAt": null,
"failureReason": null,
"failureCode": null
}
}
# Every field is camelCase. Every response is
# wrapped in "data". Timestamps are ISO 8601 UTC.ইমেইল রিকোয়েস্টের সময়েই পাঠিয়ে দেওয়া হয়। 201 রেসপন্সেই status "sent" এবং একটি sentAt টাইমস্ট্যাম্প থাকে, ফলে সিনক্রোনাস কলারের কাজ সেখানেই শেষ।
এসএমএস ও হোয়াটসঅ্যাপ গ্রহণ করে একটি ওয়ার্কারের হাতে তুলে দেওয়া হয়। 201 রেসপন্সে status থাকে "queued" — ফলাফল আসে পরে, কোনো ওয়েবহুকে অথবা আপনার পরবর্তী status রিডে।
সর্বোচ্চ 10টি ফাইল এবং ডিকোড করার পর মোট 7 MB, রিকোয়েস্ট বডিতে base64 আকারে। এসএমএস বা হোয়াটসঅ্যাপে অ্যাটাচমেন্ট সরাসরি প্রত্যাখ্যান করা হয়, নীরবে বাদ দেওয়া হয় না।
এক বসাতেই পড়ে ফেলার মতো ছোট, আর প্রতিটিই প্রকাশিত স্পেকে আছে। ওয়েবহুক সাবস্ক্রিপশন আলাদাভাবে পরিচালিত হয় — Settings থেকে, অথবা webhooks:manage স্কোপ আছে এমন কী দিয়ে।
POST /api/v1/messages
GET /api/v1/messages/{id}
GET /api/v1/messages
POST /api/v1/contacts
POST /api/v1/contacts/bulk
GET /api/v1/contacts
GET /api/v1/contacts/{id}
PATCH /api/v1/contacts/{id}
DELETE /api/v1/contacts/{id}
POST /api/v1/templates
GET /api/v1/templates
GET /api/v1/templates/{id}
GET /api/v1/analytics/messagesযেকোনো চ্যানেলে পাঠান, id দিয়ে একটি পড়ে নিন, অথবা cursor দিয়ে ইতিহাস পেজ করে দেখুন। Scopes: messages:send, messages:read.
তৈরি, পড়া, আপডেট, ডিলিট, এবং সর্বোচ্চ 1,000 সারির বাল্ক তৈরি — যা প্রতি সারির জন্য created, duplicate ও error রিপোর্ট ফেরত দেয়। Scopes: contacts:read, contacts:write.
ইমেইল বা এসএমএস টেমপ্লেট তৈরি করুন, তারপর তিনটি চ্যানেল জুড়েই তালিকা দেখুন বা নিয়ে আসুন — আপনার টিম ড্যাশবোর্ডে যেগুলো বানিয়েছে সেগুলোসহ। এখানে দেখা যেকোনো id একটি বৈধ templateId।
একটি কলেই একটি তারিখ পরিসরে প্রতি চ্যানেলের total, sent, delivered, opened, clicked, bounced ও failed ফেরত আসে। চ্যানেল ও সময়কাল অনুযায়ী সংখ্যা।
ছয়টি ফিল্ড বলে দেয় একটি মেসেজ কতদূর পৌঁছেছে এবং কেন থেমেছে: status, sentAt, deliveredAt, failedAt, failureReason ও failureCode। এগুলো স্ট্যাটাস রিডে, তালিকায় এবং প্রতিটি ওয়েবহুক পেলোডের ভেতরে থাকে।
GET /api/v1/messages?channel=sms&status=failed&limit=50
{
"data": [ /* newest first */ ],
"pagination": {
"cursor": "msg_7b21c4e9f0a3",
"hasMore": true
}
}
# Next page: &cursor=msg_7b21c4e9f0a3
# Filters: channel, status, createdAfter, createdBefore
# limit: 1-100, default 20queued গৃহীত, এখনও কোনো প্রোভাইডারকে দেওয়া হয়নি sending পাঠানোর প্রক্রিয়ায় sent প্রোভাইডার এটি গ্রহণ করেছে delivered হ্যান্ডসেট বা মেইল সার্ভার নিশ্চিত করেছে opened ইমেইল ওপেন, অথবা হোয়াটসঅ্যাপের রিড রিসিট clicked ইমেইলের লিংকে ক্লিক bounced প্রাপকের ঠিকানা এটি প্রত্যাখ্যান করেছে failed failureReason / failureCode দেখুন # Filterable by channel, status and created date. # NOT filterable by tag — tags are returned, not queried.
কী একবারই তৈরি হয় Settings → Developer API থেকে, আর দেখানোও হয় একবারই। কাজের জন্য যতটুকু স্কোপ দরকার শুধু ততটুকুই দিন, যে IP থেকে কল হবে সেগুলোতে সীমাবদ্ধ করুন, এবং একটি মেয়াদ ঠিক করে দিন। রোটেশন করলে নতুন কী সঙ্গে সঙ্গেই তৈরি হয় এবং পুরোনো কী আরও ২৪ ঘণ্টা কাজ করতে থাকে, ফলে কোনো বিরতি ছাড়াই ডিপ্লয় করতে পারেন।
X-API-Key: mk_1f9c4e8b... # or: Authorization: Bearer mk_1f9c4e8b... { "name": "Order service (production)", "permissions": ["messages:send", "messages:read"], "rateLimit": 1000, "allowedIps": ["203.0.113.9"], "expiresAt": "2027-01-31T00:00:00.000Z" } # scopes: messages:send messages:read # contacts:read contacts:write # templates:read templates:write # analytics:read webhooks:manage
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1784112300
{
"error": {
"code": "rate_limited",
"message": "Rate limit of 1000 requests per minute
exceeded. Retry after the window resets.",
"docsUrl": "/support/api-docs#errors"
}
}
# The three headers are on EVERY keyed response,
# not just the 429. Reset is a Unix timestamp.কী যিনি তৈরি করছেন তাঁর নিজের যে স্কোপ আছে, শুধু সেগুলোই কী-তে দেওয়া যায়; আর ওই ব্যক্তি নিষ্ক্রিয় হলে কী-টিও সঙ্গে সঙ্গে কাজ করা বন্ধ করে। কী-এর স্কোপের বাইরের কোনো কল করলে 403 forbidden_scope ফেরত আসে, কখনোই আংশিক ফলাফল নয়।
যেকোনো POST-এ নিজের একটি কী দিন। ২৪ ঘণ্টার মধ্যে আবার পাঠালে মূল রেসপন্সটিই হুবহু ফিরে আসে, সঙ্গে Idempotent-Replay: true হেডার — দ্বিতীয় কোনো মেসেজ যায় না। একই কী অন্য এন্ডপয়েন্টে ব্যবহার করলে 409 পাবেন।
ডিফল্ট হলো প্রতি কী-তে মিনিটে 1,000 রিকোয়েস্ট, যা প্রতিটি কী-এর জন্য আলাদা করে বদলানো যায়। প্রতিটি রেসপন্সে limit, remaining ও reset ফিরে আসে, ফলে সীমায় পৌঁছার আগেই ক্লায়েন্ট গতি কমাতে পারে।
আপনার অ্যাপের একটি URL দিন, আর আপনার কী দিয়ে পাঠানো মেসেজের প্রতিটি অবস্থা পরিবর্তন QuickGrow সেখানে পোস্ট করবে। ক্যাম্পেইনের ট্রাফিক ইচ্ছাকৃতভাবেই বাদ রাখা হয়েছে — শুধু এপিআই দিয়ে পাঠানো মেসেজই ইভেন্ট তৈরি করে, তাই কোনো বাল্ক সেন্ডে আপনার এন্ডপয়েন্ট ভেসে যায় না।
X-QuickGrow-Event: message.delivered
X-QuickGrow-Signature: t=1784107455,v1=5257a869e7ec...
{
"id": "evt_9f2c41b8d0a7",
"event": "message.delivered",
"createdAt": "2026-08-10T09:14:25.000Z",
"data": {
"message": {
"id": "msg_9f8a3bc2e41d",
"channel": "email",
"status": "delivered",
"to": "[email protected]",
"tags": ["order-confirmation"],
"metadata": { "orderId": "1042" },
"createdAt": "2026-08-10T09:14:22.000Z",
"sentAt": "2026-08-10T09:14:22.000Z",
"deliveredAt": "2026-08-10T09:14:25.000Z",
"failedAt": null,
"failureReason": null,
"failureCode": null
}
}
}import { createHmac, timingSafeEqual } from "node:crypto";
const header = req.get("X-QuickGrow-Signature");
const { t, v1 } = Object.fromEntries(
header.split(",").map((p) => p.split("=")),
);
// Reject anything stale. 300s is a sane default.
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return;
const expected = createHmac("sha256", secret)
.update(t + "." + req.rawBody)
.digest("hex");
// Compare in constant time. Sign the RAW body —
// not the object your JSON parser handed you.
timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(v1, "hex"),
);প্রতিটি ডেলিভারি প্রথমে সঙ্গে সঙ্গে চেষ্টা করা হয়, তারপর ৫ সেকেন্ড, ১ মিনিট, ৫ মিনিট ও ৩০ মিনিট পর — মোট পাঁচবার, এরপর থেমে যায়। প্রতিটি চেষ্টা তার স্ট্যাটাস কোড ও রেসপন্সসহ লগ করা হয়, আর আপনি যেকোনো সময় একটি টেস্ট ডেলিভারি পাঠাতে বা সাইনিং সিক্রেট বদলে নিতে পারেন।
⚠ রিট্রাই সর্বোচ্চ চেষ্টার ভিত্তিতে করা হয়, এটি কোনো গ্যারান্টি নয়। সত্যের উৎস হিসেবে ধরুন GET /messages -কে, এবং সেই অনুযায়ী মিলিয়ে নিন।
প্রতিটি এন্ডপয়েন্টের প্রতিটি ব্যর্থতা একই গঠনে ফিরে আসে, সঙ্গে থাকে স্থিতিশীল ও মেশিন-রিডেবল একটি কোড। কোড শুধু যোগ করা হয় — কখনও নাম বদলানো বা সরানো হয় না। ফলে আজ আপনি যে switch স্টেটমেন্ট লিখবেন, সেটি পরেও কাজ করতে থাকবে।
{
"error": {
"code": "invalid_recipient",
"message": "\"to\" must be an E.164 phone number (e.g. +8801700000000) for channel \"sms\"",
"docsUrl": "/support/api-docs#errors"
}
}| Status | When you see it | |
|---|---|---|
| unauthorized | 401 | কী নেই, অজানা কী, মেয়াদোত্তীর্ণ কী, অথবা কী যে IP অনুমোদন করে না সেখান থেকে করা কল। |
| forbidden_scope | 403 | কী বৈধ, কিন্তু এই রুটের জন্য যে স্কোপ দরকার সেটি এতে নেই। |
| rate_limited | 429 | এই মিনিটে প্রতি কী-এর সর্বোচ্চ সীমা পেরিয়ে গেছে। X-RateLimit-Reset পড়ুন এবং অপেক্ষা করুন। |
| invalid_request | 400 | কোনো ফিল্ড ভ্যালিডেশনে ব্যর্থ হয়েছে, অথবা চ্যানেল-নির্দিষ্ট আবশ্যক ফিল্ডগুলো নেই। |
| invalid_recipient | 400 | "to" is not an email address on channel email, or not E.164 on sms/whatsapp. |
| invalid_attachment | 400 | ইমেইল ছাড়া অন্য চ্যানেলে অ্যাটাচমেন্ট, অথবা ডিকোড করা যায় না এমন base64 কনটেন্ট। |
| attachment_too_large | 400 | অ্যাটাচমেন্টগুলো ডিকোড করার পর মোট 7 MB ছাড়িয়ে যায়। |
| template_not_found | 404 | এই অ্যাকাউন্টে ওই id-এর কোনো টেমপ্লেট নেই, অথবা তার চ্যানেল পাঠানোর চ্যানেলের সঙ্গে মেলে না। |
| contact_not_found | 404 | ওই id-এর কোনো কন্টাক্ট নেই। |
| message_not_found | 404 | ওই id-এর কোনো মেসেজ নেই। |
| webhook_not_found | 404 | ওই id-এর কোনো ওয়েবহুক সাবস্ক্রিপশন নেই। |
| not_found | 404 | রিকোয়েস্টে উল্লেখ করা রিসোর্সটি আর নেই। |
| conflict | 409 | এই মানগুলো নিয়ে একটি রেকর্ড আগে থেকেই আছে — সাধারণত ডুপ্লিকেট কন্টাক্ট। |
| idempotency_conflict | 409 | ওই Idempotency-Key এখনও প্রক্রিয়াধীন, অথবা অন্য একটি এন্ডপয়েন্টে আগেই ব্যবহার করা হয়েছে। |
| insufficient_credits | 402 | পাঠানোর মতো পর্যাপ্ত ব্যালেন্স নেই। টপ-আপ করে আবার চেষ্টা করুন। |
| send_blocked | 422 | কনসেন্ট, কোয়ায়েট আওয়ার, ফ্রিকোয়েন্সি ক্যাপ অথবা প্রোভাইডার পাঠানোটি প্রত্যাখ্যান করেছে। |
| internal_error | 500 | আমাদের দিকে কিছু একটা সমস্যা হয়েছে। একই Idempotency-Key দিয়ে আবার চেষ্টা করা নিরাপদ। |

এপিআই রেফারেন্স QuickGrow-এর ভেতরেই থাকে, আপনি যে কী ব্যবহার করতে যাচ্ছেন ঠিক তার পাশেই: প্রতিটি এন্ডপয়েন্ট, প্রতিটি ফিল্ড, cURL, Node, Python ও PHP-তে রিকোয়েস্ট ও রেসপন্সের নমুনা, এরর ক্যাটালগ এবং ওয়েবহুক যাচাইয়ের ধাপে ধাপে নির্দেশনা। একই জিনিস একটি OpenAPI ডকুমেন্ট আকারেও প্রকাশ করা আছে, যা Postman বা কোনো জেনারেটরে ইমপোর্ট করতে পারেন।
ইন্টিগ্রেশন লেখার আগে যা জানা দরকার, পরে নয়। এগুলোর কোনোটিই রোডম্যাপের বিষয় নয় — এপিআই আজ ঠিক এভাবেই কাজ করে।
কোনোটিই নয় — প্রকাশিত কোনো প্যাকেজ নেই। প্রতিটি এন্ডপয়েন্ট HTTPS-এর উপর সাধারণ JSON, সঙ্গে একটিমাত্র X-API-Key হেডার। ফলে আপনার ভাষায় ইতিমধ্যেই যে HTTP ক্লায়েন্ট আছে, সেটাই যথেষ্ট। রেফারেন্সে cURL, Node, Python ও PHP-তে চালিয়ে দেখার মতো নমুনা দেওয়া আছে, আর ক্লায়েন্ট দরকার হলে /api/public-docs/openapi.json-এর OpenAPI ডকুমেন্ট থেকে সেটি তৈরি করে নিতে পারবেন।
POST-এ নিজের একটি Idempotency-Key হেডার দিন। প্রথম চেষ্টাটি সত্যিই পৌঁছে থাকলে রিট্রাইতে সংরক্ষিত রেসপন্সটিই হুবহু ফিরে আসে — একই বডি, একই স্ট্যাটাস, সঙ্গে Idempotent-Replay: true হেডার — এবং দ্বিতীয় কোনো মেসেজ যায় না। কী ২৪ ঘণ্টা ধরে রাখা হয়। প্রথম চেষ্টা ব্যর্থ হলে কী ছেড়ে দেওয়া হয়, ফলে রিট্রাই সত্যিকারভাবেই চলে। একই কী দুটি ভিন্ন এন্ডপয়েন্টে ব্যবহার করলে 409 idempotency_conflict ফেরত আসে।
এসএমএস বা হোয়াটসঅ্যাপে নয়। যে মেসেজ কোনো কন্টাক্টের সঙ্গে মেলে, তার প্রতিটির ক্ষেত্রেই পাঠানোর সময় কোয়ায়েট আওয়ার কার্যকর হয় — আর পাবলিক এপিআই সবসময়ই কন্টাক্ট মিলিয়ে নেয়, ট্রানজেকশনাল পাঠানোসহ। বাংলাদেশে সময়সীমা 21:00–08:00 Asia/Dhaka, এবং কলটি সকালের জন্য কিউ না করে 422 send_blocked ফেরত দেয়। ইমেইলে এই বাধা নেই, তাই সময়-সংবেদনশীল কোড ইমেইলে অথবা দিনের বেলার মধ্যে পাঠানোই সবচেয়ে নিরাপদ।
এপিআই রেফারেন্স উন্মুক্ত এবং স্পেক মেশিন-রিডেবল। একটি অ্যাকাউন্ট খুলুন, Settings → Developer API থেকে একটি স্কোপ করা কী তৈরি করুন — আপনার প্রথম মেসেজ তখন মাত্র একটি POST দূরে।
এক প্ল্যাটফর্ম থেকেই সব চ্যানেল চালান। নিজের SMTP, এসএমএস গেটওয়ে ও হোয়াটসঅ্যাপ নম্বর ব্যবহার করুন — অথবা আমাদেরটি নিন।
দাম দেখুন →