A Node.js library to unify and manage courier services in Bangladesh (Steadfast, Pathao, REDX, CarryBee, PaperFly, EcoCourier).
v1.2.0 — Adds the PaperFly and EcoCourier providers for a total of six unified couriers. See the CHANGELOG for details.
- Unified API for multiple courier providers (Steadfast, Pathao, REDX, CarryBee, PaperFly, EcoCourier)
- Create single or bulk orders
- Fetch order status by consignment ID, invoice, or tracking code
- Manage Pathao tokens, stores, and locations
- Steadfast return requests, payments, and police stations
- REDX store management, parcel tracking, and price calculation
- CarryBee store management, reverse pickup, exchanges, and address resolution
- PaperFly order creation, tracking, and cancellation
- EcoCourier order creation, tracking, cancellation, package listing, payment status, fraud check, and location/branch data
- Get Steadfast account balance
- Price calculation for Pathao and REDX orders
- Webhook support for Steadfast, Pathao, REDX, CarryBee, and PaperFly with built-in verification
Install with npm or pnpm:
npm install routexpress-bd
# or
pnpm add routexpress-bd// Default import
import RouteXpress from "routexpress-bd";
// Or named import
import { RouteXpress } from "routexpress-bd";
const client = new RouteXpress({
steadfast: {
apiKey: "YOUR_STEADFAST_API_KEY",
apiSecret: "YOUR_STEADFAST_API_SECRET",
},
pathao: {
apiKey: "YOUR_PATHAO_API_KEY",
apiSecret: "YOUR_PATHAO_API_SECRET",
username: "YOUR_PATHAO_USERNAME",
password: "YOUR_PATHAO_PASSWORD",
},
redx: {
apiKey: "YOUR_REDX_API_KEY",
environment: "production", // or "development"
},
carrybee: {
clientId: "YOUR_CARRYBEE_CLIENT_ID",
clientSecret: "YOUR_CARRYBEE_CLIENT_SECRET",
clientContext: "YOUR_CARRYBEE_CLIENT_CONTEXT",
environment: "sandbox", // or "production"
},
paperfly: {
username: "YOUR_PAPERFLY_USERNAME",
password: "YOUR_PAPERFLY_PASSWORD",
apiKey: "YOUR_PAPERFLY_API_KEY",
},
ecourier: {
apiKey: "YOUR_ECOURIER_API_KEY",
apiSecret: "YOUR_ECOURIER_API_SECRET",
userId: "YOUR_ECOURIER_USER_ID",
environment: "sandbox", // or "production"
},
});You can configure one or more providers. At least one must be configured.
Each provider can have its own webhook endpoint. Add the webhooks config to enable them:
const client = new RouteXpress({
steadfast: { apiKey: "...", apiSecret: "..." },
pathao: { apiKey: "...", apiSecret: "...", username: "...", password: "..." },
redx: { apiKey: "...", environment: "production" },
webhooks: {
steadfast: {
enabled: true,
webhookUrl: "https://your-server.com/api/webhooks/steadfast",
apiSecret: "YOUR_STEADFAST_WEBHOOK_SECRET",
},
pathao: {
enabled: true,
webhookUrl: "https://your-server.com/api/webhooks/pathao",
integrationSecret: "YOUR_PATHAO_INTEGRATION_SECRET",
},
redx: {
enabled: true,
webhookUrl: "https://your-server.com/api/webhooks/redx",
apiAccessToken: "YOUR_REDX_API_ACCESS_TOKEN",
},
carrybee: {
enabled: true,
webhookUrl: "https://your-server.com/api/webhooks/carrybee",
webhookSecret: "YOUR_CARRYBEE_WEBHOOK_SECRET",
},
paperfly: {
enabled: true,
webhookUrl: "https://your-server.com/api/webhooks/paperfly",
webhookSecret: "YOUR_PAPERFLY_WEBHOOK_SECRET",
},
},
});| Provider | Verification Method | Secret Field |
|---|---|---|
| Steadfast | Authorization: Bearer <apiSecret> header |
apiSecret |
| Pathao | X-Pathao-Merchant-Webhook-Integration-Secret header + optional HMAC-SHA256 X-PATHAO-Signature |
integrationSecret |
| RedX | API-ACCESS-TOKEN header |
apiAccessToken |
| CarryBee | X-Carrybee-Webhook-Signature header |
webhookSecret |
| PaperFly | X-Paperfly-Webhook-Secret header |
webhookSecret |
EcoCourier does not currently expose a webhook handler.
const response = await client.createOrder("steadfast", {
order_details: {
invoice: "INV90444w0144",
recipient_name: "John Doe",
recipient_phone: "01712344578",
recipient_address: "123 Main Street, Dhaka",
cod_amount: 1000,
note: "Deliver within 3 PM",
alternative_phone: "01712345678",
recipient_email: "john@example.com",
item_description: "Electronics",
total_lot: 1,
delivery_type: 0, // 0 = home delivery, 1 = point delivery/hub pickup
},
});
console.log("Steadfast Order Response:", response);const response = await client.createBulkOrder("steadfast", {
orders: [
{
invoice: "INV0094e8981",
recipient_name: "John Doe",
recipient_phone: "01712345678",
recipient_address: "123 Main Street, Dhaka",
cod_amount: 1000,
},
{
invoice: "INV009849e82",
recipient_name: "Jane Smith",
recipient_phone: "01787654321",
recipient_address: "456 Elm Street, Chittagong",
cod_amount: 2000,
},
],
});
console.log("Bulk Order Response:", response);const response = await client.getOrderStatus({
provider: "steadfast",
data: { consignment_id: "DT0905255BZNFQ" },
});
console.log("Response:", response);const response = await client.statusByInvoice("INV90444344w0144");
console.log("Response:", response);const response = await client.statusBytrackingcode("88D026EF9");
console.log("Response:", response);const response = await client.getSteadFastBalance();
console.log("Response:", response);const response = await client.createSteadfastReturnRequest({
consignment_id: 10000042,
reason: "Customer requested return",
});
console.log("Response:", response);You can identify the consignment using any one of consignment_id, invoice, or tracking_code.
const response = await client.getSteadfastReturnRequest(1);
console.log("Response:", response);const response = await client.getSteadfastReturnRequests();
console.log("Response:", response);const response = await client.getSteadfastPayments();
console.log("Response:", response);const response = await client.getSteadfastSinglePayment(1);
console.log("Response:", response);const response = await client.getSteadfastPoliceStations();
console.log("Response:", response);const response = await client.createPathaoToken();
console.log("Access Token:", response.access_token);const accessTokenResponse = await client.createPathaoToken();
const refreshToken = accessTokenResponse.refresh_token;
const response = await client.createPathaoRefreshToken(refreshToken);
console.log("Response:", response);const response = await client.createOrder("pathao", {
authToken: "YOUR_PATHAO_AUTH_TOKEN",
store_id: 148430,
merchant_order_id: "ORD123456",
recipient_name: "John Doe",
recipient_phone: "01712325678",
recipient_address: "123 Main Street, Dhaka",
recipient_city: 1,
recipient_zone: 1,
recipient_area: 1,
delivery_type: 48,
item_type: 2,
special_instruction: "Handle with care",
item_quantity: 1,
item_weight: 0.5,
item_description: "Electronics",
amount_to_collect: 1000,
});
console.log("Pathao Order Response:", response);const response = await client.createBulkOrder("pathao", {
authToken: "YOUR_PATHAO_AUTH_TOKEN",
orders: [
{
store_id: 148430,
merchant_order_id: "ORD123456",
recipient_name: "John Doe",
recipient_phone: "01712342578",
recipient_address: "123 Main Street, Dhaka",
recipient_city: 1,
recipient_zone: 1,
recipient_area: 1,
delivery_type: 48,
item_type: 2,
special_instruction: "Handle with care",
item_quantity: 1,
item_weight: 0.5,
item_description: "Electronics",
amount_to_collect: 1000,
},
],
});
console.log("Bulk Order Response:", response);const response = await client.getOrderStatus({
provider: "pathao",
data: {
consignment_id: "DT0905255BZNFQ",
authToken: "YOUR_PATHAO_AUTH_TOKEN",
},
});
console.log("Response:", response);const response = await client.createPathaoStore("YOUR_PATHAO_AUTH_TOKEN", {
name: "My Store",
contact_name: "John Doe",
contact_number: "01712345678",
secondary_contact: "01787654321",
address: "123 Main Street, Dhaka",
city_id: 1,
zone_id: 1,
area_id: 1,
});
console.log("Store Name:", response.store_name);const response = await client.getAllPathaoStore("YOUR_PATHAO_AUTH_TOKEN");
console.log("Response:", response);const response = await client.getPathaoCity("YOUR_PATHAO_AUTH_TOKEN");
console.log("Response:", response);const response = await client.getPathaoZone("YOUR_PATHAO_AUTH_TOKEN", 1);
console.log("Response:", response);const response = await client.getPathaoArea("YOUR_PATHAO_AUTH_TOKEN", 1);
console.log("Response:", response);const response = await client.price_plane("YOUR_PATHAO_AUTH_TOKEN", {
store_id: 148430,
item_type: 2,
recipient_city: 1,
recipient_zone: 1,
recipient_area: 1,
delivery_type: 48,
item_weight: 5,
item_quantity: 1,
amount_to_collect: 1000,
});
console.log("Response:", response);const response = await client.createOrder("redx", {
customer_name: "John Doe",
customer_phone: "01712345628",
delivery_area: "Dhaka",
delivery_area_id: 1,
customer_address: "123 Main Street, Dhaka",
merchant_invoice_id: "INV123456",
cash_collection_amount: "1000",
parcel_weight: "2",
instruction: "Handle with care",
value: "1500",
pickup_store_id: "504839",
parcel_details_json: [
{ name: "Electronics Item 1", category: "Electronics", value: "1000" },
],
});
console.log("Redx Order Response:", response);const response = await client.createRedXStore({
name: "My Store",
address: "123 Main Street, Dhaka",
phone: "01712345678",
area_id: "1",
});
console.log("Store Response:", response);const response = await client.trackParcelByTrackingCode("25A510SA18UXL7");
console.log("Response:", response);const response = await client.getParcelInfoByTrackingCode("25A510SA18UXL7");
console.log("Response:", response);const response = await client.updateParcel("25A510SA18UXL7", {
property_name: "status",
new_value: "cancelled",
reason: "Customer requested change",
});
console.log("Response:", response);const response = await client.getRedXStores();
console.log("Response:", response);const response = await client.getPickupStoreInfo("504839");
console.log("Response:", response);const response = await client.getRedXAreaList();
console.log("Response:", response);const response = await client.getResXAreabyPostcode("1204");
console.log("Response:", response);const response = await client.getRedXAreaBYDistrict_name("Dhaka");
console.log("Response:", response);const response = await client.calculateRedXPrice({
delivery_area_id: 1,
pickup_area_id: 1,
cash_collection_amount: 1000,
weight: 2,
});
console.log("Response:", response);const response = await client.createOrder("carrybee", {
store_id: "9922",
merchant_order_id: "order-1234",
delivery_type: 1, // 1 = Normal, 2 = Express
product_type: 1, // 1 = Parcel, 2 = Book, 3 = Document
recipient_phone: "01712345678",
recipient_name: "John Doe",
recipient_address: "House 10, Road 5, Dhanmondi, Dhaka",
city_id: 14,
zone_id: 50,
area_id: 1676,
item_weight: 500,
item_quantity: 1,
collectable_amount: 1500,
});
console.log("CarryBee Order Response:", response);const response = await client.getCarrybeeOrderDetails("F1008BB2A9R");
console.log("Response:", response);const response = await client.cancelCarrybeeOrder(
"F1008BB2A9R",
"Customer requested cancellation",
);
console.log("Response:", response);const response = await client.createCarrybeeStore({
name: "My Store",
contact_person_name: "John Doe",
contact_person_number: "01712345678",
address: "123 Main Street, Dhaka",
city_id: 14,
zone_id: 50,
area_id: 1676,
});
console.log("Response:", response);const response = await client.getCarrybeeStores();
console.log("Response:", response);const response = await client.getCarrybeeCities();
console.log("Response:", response);const response = await client.getCarrybeeZones(14); // 14 = Dhaka
console.log("Response:", response);const response = await client.getCarrybeeAreas(14, 151); // city 14, zone 151
console.log("Response:", response);const response = await client.searchCarrybeeAreas("Gulshan");
console.log("Response:", response);const response = await client.getCarrybeeAddressDetails(
"House 5, Road 3, Banani, Dhaka",
);
console.log("Response:", response);const response = await client.createCarrybeeReversePickup({
store_id: "9922",
product_type: 1,
customer_phone: "01712345678",
customer_name: "John Doe",
customer_address: "House 10, Road 5, Dhanmondi, Dhaka",
city_id: 14,
zone_id: 50,
area_id: 1676,
item_weight: 500,
});
console.log("Response:", response);const response = await client.createCarrybeeExchange("F1008BB2A9R", {
merchant_order_id: "exchange-001",
item_quantity: 1,
item_weight: 500,
});
console.log("Response:", response);const response = await client.createPaperflyOrder({
merchantOrderReference: "order-1234",
storeName: "My Store",
productBrief: "Electronics",
packagePrice: "1500",
max_weight: "1",
customerName: "John Doe",
customerAddress: "House 10, Road 5, Dhanmondi, Dhaka",
customerPhone: "01712345678",
orderType: "regular", // optional
});
console.log("PaperFly Order Response:", response);const response = await client.trackPaperflyOrder("order-1234");
console.log("Response:", response);const response = await client.cancelPaperflyOrder("order-1234");
console.log("Response:", response);const response = await client.createEcourierOrder({
recipient_name: "John Doe",
recipient_mobile: "01712345678",
recipient_city: "Dhaka",
recipient_thana: "Dhanmondi",
recipient_area: "Dhanmondi 15",
recipient_address: "House 10, Road 5, Dhanmondi, Dhaka",
package_code: "PACK-001",
product_price: 1500,
payment_method: "COD",
recipient_zip: "1209",
number_of_item: 1,
comments: "Handle with care",
});
console.log("EcoCourier Order Response:", response);// Track by product ID and/or EcoCourier reference (ecr)
const response = await client.trackEcourierOrder("PACK-001", "ECR123456");
console.log("Response:", response);const response = await client.cancelEcourierOrder(
"ECR123456",
"Customer requested cancellation",
);
console.log("Response:", response);const response = await client.getEcourierPackages();
console.log("Response:", response);const response = await client.ecourierFraudCheck("01712345678");
console.log("Response:", response);const response = await client.getEcourierCities();
console.log("Response:", response);const response = await client.getEcourierThanas("Dhaka");
console.log("Response:", response);const response = await client.getEcourierAreas("1209");
console.log("Response:", response);const response = await client.getEcourierBranches();
console.log("Response:", response);const response = await client.getEcourierPaymentStatus("ECR123456");
console.log("Response:", response);const response = await client.trackEcourierChild("PACK-001", "ECR123456");
console.log("Response:", response);const response = await client.cancelEcourierChildOrder(
"ECR123456",
"Customer requested cancellation",
);
console.log("Response:", response);RouteXpress provides built-in webhook handlers for each provider. Use them in your route handlers to verify and parse incoming webhooks.
// Express example
app.post("/api/webhooks/steadfast", (req, res) => {
const handler = client.getSteadfastWebhook();
// Verify + parse in one step
const result = handler.handle(req.body, req.headers);
if (!result.success) {
return res.status(400).json({ error: result.error });
}
const webhook = result.data;
// Handle delivery status updates
if (webhook.notification_type === "delivery_status") {
console.log(`Order ${webhook.consignment_id}: ${webhook.current_status}`);
}
// Handle tracking updates
if (webhook.notification_type === "tracking_update") {
console.log(`Tracking update for ${webhook.consignment_id}`);
}
res.json({ received: true });
});// Express example — integration secret verification
app.post("/api/webhooks/pathao", (req, res) => {
const handler = client.getPathaoWebhook();
// Verify via X-Pathao-Merchant-Webhook-Integration-Secret header
const result = handler.handle(req.body, req.headers);
if (!result.success) {
return res.status(400).json({ error: result.error });
}
console.log("Pathao event:", result.data.event);
console.log("Consignment:", result.data.consignment_id);
res.json({ received: true });
});
// Or verify via HMAC-SHA256 signature (X-PATHAO-Signature header)
app.post("/api/webhooks/pathao/signature", (req, res) => {
const handler = client.getPathaoWebhook();
const result = handler.handleWithSignature(req.body, req.headers);
if (!result.success) {
return res.status(400).json({ error: result.error });
}
console.log("Pathao event:", result.data.event);
res.json({ received: true });
});// Express example
app.post("/api/webhooks/redx", (req, res) => {
const handler = client.getRedXWebhook();
// Verify via API-ACCESS-TOKEN header
const result = handler.handle(req.body, req.headers);
if (!result.success) {
return res.status(400).json({ error: result.error });
}
const webhook = result.data;
console.log(`Parcel ${webhook.tracking_number}: ${webhook.status}`);
res.json({ received: true });
});// Express example
app.post("/api/webhooks/carrybee", (req, res) => {
const handler = client.getCarrybeeWebhook();
// Verify via X-Carrybee-Webhook-Signature header
const result = handler.handle(req.body, req.headers);
if (!result.success) {
return res.status(400).json({ error: result.error });
}
const webhook = result.data;
console.log(`CarryBee event: ${webhook.event}`);
console.log(`Consignment: ${webhook.consignment_id}`);
console.log(`Merchant Order: ${webhook.merchant_order_id}`);
res.json({ received: true });
});// Express example
app.post("/api/webhooks/paperfly", (req, res) => {
const handler = client.getPaperflyWebhook();
// Verify via X-Paperfly-Webhook-Secret header
const result = handler.handle(req.body, req.headers);
if (!result.success) {
return res.status(400).json({ error: result.error });
}
const webhook = result.data;
console.log(`PaperFly event: ${webhook.event}`);
console.log(`Merchant Order: ${webhook.merchant_order_reference}`);
console.log(`Tracking: ${webhook.tracking_number}`);
res.json({ received: true });
});Use getWebhookUrl() to retrieve the URL you registered with each provider:
const steadfastUrl = client.getWebhookUrl("steadfast");
//=> "https://your-server.com/api/webhooks/steadfast"
const pathaoUrl = client.getWebhookUrl("pathao");
//=> "https://your-server.com/api/webhooks/pathao"
const redxUrl = client.getWebhookUrl("redx");
//=> "https://your-server.com/api/webhooks/redx"
const carrybeeUrl = client.getWebhookUrl("carrybee");
//=> "https://your-server.com/api/webhooks/carrybee"
const paperflyUrl = client.getWebhookUrl("paperfly");
//=> "https://your-server.com/api/webhooks/paperfly"| Provider | Header | Method |
|---|---|---|
| Steadfast | Authorization: Bearer <apiSecret> |
handler.handle(body, headers) |
| Pathao (secret) | X-Pathao-Merchant-Webhook-Integration-Secret |
handler.handle(body, headers) |
| Pathao (signature) | X-PATHAO-Signature (HMAC-SHA256) |
handler.handleWithSignature(body, headers) |
| RedX | API-ACCESS-TOKEN |
handler.handle(body, headers) |
| CarryBee | X-Carrybee-Webhook-Signature |
handler.handle(body, headers) |
| PaperFly | X-Paperfly-Webhook-Secret |
handler.handle(body, headers) |
// Default export
import RouteXpress from "routexpress-bd";
// Named export — same class
import { RouteXpress } from "routexpress-bd";
// Webhook handlers
import {
SteadfastWebhookHandler,
PathaoWebhookHandler,
RedXWebhookHandler,
CarrybeeWebhookHandler,
PaperflyWebhookHandler,
} from "routexpress-bd";
// Webhook types
import type {
// Steadfast
SteadfastWebhookPayload,
SteadfastDeliveryStatusWebhook,
SteadfastTrackingUpdateWebhook,
// Pathao
PathaoWebhookPayload,
PathaoWebhookEvent,
PathaoWebhookBase,
// RedX
RedXWebhookPayload,
RedXWebhookStatus,
RedXDeliveryType,
// CarryBee
CarrybeeWebhookPayload,
CarrybeeWebhookEvent,
// PaperFly
PaperflyWebhookPayload,
PaperflyWebhookEvent,
// Common
WebhookVerifyResult,
WebhookParseResult,
} from "routexpress-bd";
// Config types
import type {
Config,
Steadfast_Config,
Pathao_Config,
Redx_Config,
Carrybee_Config,
Paperfly_Config,
Ecourier_Config,
SteadfastWebhookConfig,
PathaoWebhookConfig,
RedXWebhookConfig,
CarrybeeWebhookConfig,
PaperflyWebhookConfig,
} from "routexpress-bd";All methods throw errors on failure. Use try/catch to handle errors:
try {
const response = await client.createOrder("steadfast", orderData);
} catch (err) {
console.error(err);
}- Always keep your API keys and tokens secure. Do not hardcode them in production code.
- Use async/await and proper error handling for all API calls.
- Refer to the official documentation of each courier for more details on their specific requirements.
MIT
For more details, see the CHANGELOG.
