Webhooks ও ইন্টিগ্রেশন API (Custom Store)
API key দিয়ে নিজের ওয়েবসাইট থেকে প্রোডাক্ট, কাস্টমার ও অর্ডার EcomUp-এ পাঠান, আর সাইন করা অর্ডার webhook পান।
Custom Store ইন্টিগ্রেশন দিয়ে আপনার নিজের ওয়েবসাইট EcomUp-এর সাথে যুক্ত করুন। আপনার সাইট একটি সহজ API দিয়ে প্রোডাক্ট, ক্যাটাগরি, কাস্টমার, ঠিকানা ও অর্ডার EcomUp-এ পাঠায়। আর অর্ডার তৈরি হলে বা স্ট্যাটাস বদলালে EcomUp একটি webhook-এর মাধ্যমে আপনার সাইটে ইভেন্ট ফেরত পাঠায় (webhook মানে আপনার বেছে নেওয়া একটি URL-এ স্বয়ংক্রিয় মেসেজ)। এই গাইডটি তার জন্য লেখা, যিনি আপনার ওয়েবসাইট তৈরি করেন।
শুরু করার আগে
- আপনার রোলে কানেকশন ম্যানেজ করার অনুমতি থাকতে হবে (owner ও admin-দের ডিফল্টভাবে থাকে)।
- আপনার ওয়েবসাইটে এমন একটি সার্ভার লাগবে যা HTTPS রিকোয়েস্ট পাঠাতে পারে, আর webhook পেতে একটি পাবলিক HTTPS URL লাগবে।
- Custom Store আপনার জন্য ঠিক কিনা নিশ্চিত না হলে আগে ডেভেলপার ওভারভিউ পড়ুন। WooCommerce ও Shopify স্টোরের জন্য আলাদা নো-কোড কানেকশন আছে।
ধাপসমূহ
- বাম পাশের মেনু থেকে Connections খুলুন, Custom Store খুঁজে Connect-এ ক্লিক করুন।
- Connect Custom Store-এ ক্লিক করুন।
- API key — send as x-api-key header-এর নিচে দেখানো API key কপি করুন। এটা শুধু একবারই দেখানো হয়।

এখনই আপনার API key (1) কপি করুন (Regenerate (2) নতুন কী বানায়); নিচে এন্ডপয়েন্টগুলোর তালিকা (3)। - Inbound — API তালিকা থেকে এন্ডপয়েন্টের URL-গুলো কপি করুন। ডকুমেন্টেশন প্যানেলে প্রতিটির পাশে কপি বাটন আছে।
- Outbound — Order Webhook-এর নিচে Your API endpoint-এ আপনার URL লিখুন (যেমন https://yourstore.com/api/webhooks/orders), তারপর Save Webhook-এ ক্লিক করুন।
- Signing secret-এর পাশের চোখের আইকনে ক্লিক করে secret কপি করুন। webhook সত্যিই EcomUp থেকে এসেছে কিনা আপনার সার্ভার এটা দিয়ে যাচাই করবে।

Your API endpoint লিখুন (1), Save Webhook (2) চাপুন, আর চোখের আইকন দিয়ে Signing secret (3) কপি করুন। - আপনার সার্ভার থেকে একটি টেস্ট প্রোডাক্ট পাঠান, তারপর Products-এ সেটা এসেছে কিনা দেখুন।
টিপস
- এই ক্রমে পাঠান: আগে ক্যাটাগরি ও প্রোডাক্ট, তারপর কাস্টমার ও ঠিকানা, তারপর অর্ডার। অর্ডারের line item
externalProductIdদিয়ে প্রোডাক্টের সাথে যুক্ত হয়। externalIdহলো প্রতিটি রেকর্ডের জন্য আপনার নিজের ID। ডুপ্লিকেট এড়াতে EcomUp এটা ব্যবহার করে, তাই এটা বদলাবেন না।- অর্ডারের সাথে সবসময়
"currency": "BDT"(বা আপনার স্টোর কারেন্সি) পাঠান। না পাঠালে অর্ডারটি USD-তে সেভ হয়। - প্রোডাক্টের দাম string হিসেবে (
"49.99") আর অর্ডারের line item-এর দাম number হিসেবে (49.99) পাঠান। - অচেনা ফিল্ড গ্রহণ করা হয় না। শুধু নিচে দেওয়া ফিল্ডগুলো পাঠান।
- Webhook ডেলিভারি আবার চেষ্টা (retry) করা হয় না, আর EcomUp আপনার উত্তরের জন্য সর্বোচ্চ ১০ সেকেন্ড অপেক্ষা করে। দ্রুত 2xx রেসপন্স দিন, সময়সাপেক্ষ কাজ পরে করুন।
- Key ফাঁস হয়ে গেলে Regenerate-এ ক্লিক করুন। পুরনো key সাথে সাথে বন্ধ হয়ে যায়।
সমস্যা সমাধান
“Missing x-api-key header” বা “Invalid API key” (401)
Key-টি ঠিক x-api-key নামের হেডারে পাঠান। key রিজেনারেট করে থাকলে বা Custom Store ডিসকানেক্ট করে থাকলে নতুন key দিয়ে সার্ভার আপডেট করুন।
“price must be a non-negative number (e.g. 10 or 10.99)” (400)
প্রোডাক্টের দাম শুধু অঙ্ক ও ঐচ্ছিক দশমিক বিন্দু দিয়ে লেখা string হতে হবে, কোনো কারেন্সি চিহ্ন বা কমা ছাড়া।
“property … should not exist” (400)
আপনি এমন একটি ফিল্ড পাঠিয়েছেন যা EcomUp গ্রহণ করে না। সেটা সরিয়ে আবার চেষ্টা করুন।
“customerExternalId not found — create the customer first via POST /webhook/custom-store/customers” (400)
কাস্টমারের ঠিকানা যোগ করার আগে কাস্টমার তৈরি করুন।
“Provide either customer or customerId” (400)
প্রতিটি অর্ডারে একজন কাস্টমার লাগবে। একটি customer অবজেক্ট পাঠান, অথবা customers এন্ডপয়েন্ট থেকে পাওয়া customerId পাঠান।
অর্ডারের আইটেম প্রোডাক্টের সাথে যুক্ত না হয়ে দেখাচ্ছে
কোনো line item-এর externalProductId আপনার পাঠানো কোনো প্রোডাক্টের সাথে মেলেনি। আগে সেই প্রোডাক্টটি একই ID-কে externalId হিসেবে দিয়ে পাঠান।
আমি webhook পাচ্ছি না
URL পাবলিক কিনা, HTTPS কিনা, আর ১০ সেকেন্ডের মধ্যে উত্তর দেয় কিনা দেখুন। মনে রাখুন, order.created পাঠানো হয় EcomUp ড্যাশবোর্ডে বা AI এজেন্টের তৈরি অর্ডারের জন্য, আপনার নিজের পাঠানো অর্ডারের জন্য নয় (যাতে আপনার ডেটা আপনার কাছেই ফেরত না যায়)।
আমার সার্ভারে “Invalid signature” আসছে
JSON পার্স করার আগে, যেমনভাবে রিকোয়েস্ট বডি এসেছে ঠিক সেই raw বডির উপর HMAC হিসাব করুন, আর বর্তমান signing secret ব্যবহার করুন।
ডেভেলপারদের জন্য
Authentication ও base URL
সব inbound রিকোয়েস্ট JSON বডিসহ POST, সাথে দুটি হেডার। প্রতিটি এন্ডপয়েন্টের পুরো URL Custom Store পেজ থেকে কপি করুন; এটা দেখতে https://api.ecomup.ai/webhook/custom-store/products-এর মতো।
POST /webhook/custom-store/{resource}
Content-Type: application/json
x-api-key: YOUR_API_KEY
এন্ডপয়েন্ট
| এন্ডপয়েন্ট | কী করে | রেসপন্স |
|---|---|---|
/webhook/custom-store/products | প্রোডাক্ট তৈরি বা আপডেট করে (externalId দিয়ে মেলানো হয়) | { "success": true, "product": { … } } |
/webhook/custom-store/categories | নাম দিয়ে ক্যাটাগরি খোঁজে বা তৈরি করে | { "success": true, "category": { "id", "name", "slug" } } |
/webhook/custom-store/customers | কাস্টমার খোঁজে বা তৈরি করে | { "success": true, "customerId": "…" } |
/webhook/custom-store/addresses | কাস্টমারের সাথে শিপিং ঠিকানা যোগ করে | { "success": true, "addressId": "…" } |
/webhook/custom-store/orders | line item, কাস্টমার ও ঠিকানাসহ অর্ডার তৈরি করে | { "success": true } |
Products
| ফিল্ড | টাইপ | বাধ্যতামূলক | নোট |
|---|---|---|---|
externalId | string | হ্যাঁ | আপনার প্রোডাক্ট ID (ডুপ্লিকেট এড়ানোর key), সর্বোচ্চ ২৫৫ |
name | string | হ্যাঁ | সর্বোচ্চ ২৫৫ |
price | string (decimal) | হ্যাঁ | বর্তমান বিক্রয়মূল্য, যেমন "1250" বা "49.99" |
sku | string | না | |
regularPrice | string (decimal) | না | না দিলে price |
salePrice | string (decimal) | না | |
description, shortDescription | string | না | |
imageUrl | string (URL) | না | মূল ছবি |
images | string[] (URLs) | না | গ্যালারি |
stockQuantity | number | না | |
stockStatus | enum | না | instock, outofstock, onbackorder |
status | enum | না | draft, publish, pending |
category | string | না | ক্যাটাগরির নাম; না থাকলে তৈরি হয় |
curl -X POST https://api.ecomup.ai/webhook/custom-store/products \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "sku-123",
"name": "Cotton Panjabi",
"price": "1250",
"regularPrice": "1500",
"category": "Panjabi",
"imageUrl": "https://cdn.example.com/panjabi.jpg",
"stockQuantity": 20
}'
Categories
| ফিল্ড | টাইপ | বাধ্যতামূলক | নোট |
|---|---|---|---|
name | string | হ্যাঁ | খোঁজা বা তৈরি করা হয়; বারবার পাঠানো নিরাপদ |
Customers
| ফিল্ড | টাইপ | বাধ্যতামূলক | নোট |
|---|---|---|---|
externalId | string | হ্যাঁ | আপনার কাস্টমার ID (ডুপ্লিকেট এড়ানোর key) |
firstName | string | হ্যাঁ | |
lastName | string | না | |
phone | string | না |
Addresses
| ফিল্ড | টাইপ | বাধ্যতামূলক | নোট |
|---|---|---|---|
customerExternalId | string | হ্যাঁ | কাস্টমার আগে থেকেই থাকতে হবে |
addressLine1 | string | হ্যাঁ | সর্বোচ্চ ২৫৫ |
city | string | হ্যাঁ | সর্বোচ্চ ৬০ |
addressLine2 | string | না | সর্বোচ্চ ২৫৫ |
state | string | না | সর্বোচ্চ ৬০ |
postalCode | string | না | সর্বোচ্চ ২০ |
country | string | না | সর্বোচ্চ ৪০ |
phone | string | না | সর্বোচ্চ ২০ |
Orders
| ফিল্ড | টাইপ | বাধ্যতামূলক | নোট |
|---|---|---|---|
externalId | string | হ্যাঁ | আপনার অর্ডার ID (ডুপ্লিকেট এড়ানোর key) |
status | enum | না | pending (ডিফল্ট), processing, on-hold, completed, cancelled, refunded, failed, paid |
paymentMethod | string | না | যেমন cod |
paymentStatus | string | না | ডিফল্ট pending |
currency | string | না | ৩ অক্ষরের কোড। ডিফল্ট USD, তাই BDT পাঠান |
customerId | uuid | এর যেকোনো একটি | /customers থেকে পাওয়া ID |
customer | object | এর যেকোনো একটি | externalId, firstName বাধ্যতামূলক; lastName, phone ঐচ্ছিক |
shippingAddressId | uuid | না | /addresses থেকে পাওয়া ID; address-এর চেয়ে অগ্রাধিকার পায় |
address | object | না | Addresses এন্ডপয়েন্টের মতোই ফিল্ড, customerExternalId ছাড়া |
lineItems | array (কমপক্ষে ১টি) | হ্যাঁ | প্রতিটিতে: externalProductId (string), quantity (number, ≥ ১), price (number, একক দাম ≥ ০), ঐচ্ছিক productName |
customerNote | string | না |
{
"externalId": "order-001",
"status": "pending",
"currency": "BDT",
"paymentMethod": "cod",
"customer": { "externalId": "cust-123", "firstName": "Rahim", "phone": "01712345678" },
"address": { "addressLine1": "House 12, Road 5, Dhanmondi", "city": "Dhaka" },
"lineItems": [{ "externalProductId": "sku-123", "quantity": 2, "price": 1250 }]
}
Outbound webhook ইভেন্ট
আপনার সেভ করা URL-এ EcomUp Content-Type: application/json-সহ একটি POST পাঠায়।
| ইভেন্ট | কখন পাঠানো হয় | Data key |
|---|---|---|
order.created | EcomUp ড্যাশবোর্ডে কোনো অর্ডার তৈরি হলে (ডুপ্লিকেট করা অর্ডারসহ) বা AI সেলস এজেন্ট অর্ডার তৈরি করলে | order |
order.updated | EcomUp-এ কোনো অর্ডারের স্ট্যাটাস বদলালে | order |
product.created | EcomUp-এ প্রোডাক্ট তৈরি হলে (ড্যাশবোর্ড বা AI) | product |
customer.created | EcomUp-এ কাস্টমার তৈরি হলে | customer |
category.created | EcomUp-এ ক্যাটাগরি তৈরি হলে | category |
Inbound API দিয়ে আপনি যে ডেটা পাঠান, তা আপনার কাছে ফেরত পাঠানো হয় না। অর্ডার ইভেন্টে line item, কাস্টমার ও শিপিং ঠিকানাসহ পুরো অর্ডার থাকে।
POST https://yourstore.com/api/webhooks/orders
Content-Type: application/json
X-EcomUp-Signature: sha256=5f2b…
{
"event": "order.updated",
"order": {
"id": "…",
"orderNumber": 1042,
"status": "paid",
"amount": "2500.00",
"currency": "BDT",
"paymentMethod": "cod",
"paymentStatus": "paid",
"lineItems": [ { "productId": "…", "productName": "Cotton Panjabi", "quantity": 2, "price": 1250, "total": 2500 } ],
"customer": { "id": "…", "firstName": "Rahim", "phone": "01712345678" },
"shippingAddress": { "id": "…", "addressLine1": "House 12, Road 5, Dhanmondi", "city": "Dhaka" }
},
"timestamp": "2026-10-30T12:34:56.789Z"
}
Signature যাচাই করুন
X-EcomUp-Signature হলো sha256= এবং তারপর raw রিকোয়েস্ট বডির hex HMAC-SHA256, যেখানে key হিসেবে আপনার signing secret ব্যবহার হয়। না মিললে রিকোয়েস্টটি বাতিল করুন।
// Node.js (Express): raw বডি পেতে express.raw() ব্যবহার করুন
const crypto = require("crypto");
const expected = "sha256=" +
crypto.createHmac("sha256", SIGNING_SECRET).update(rawBody).digest("hex");
const received = req.header("X-EcomUp-Signature") || "";
const ok = expected.length === received.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
if (!ok) return res.status(401).send("Invalid signature");
res.sendStatus(200);
<?php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_ECOMUP_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $payload, $signingSecret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
http_response_code(200);