| name | shipping-carriers |
| description | Shipping carrier integrations for DropFlow — Shiprocket (domestic India), Delhivery (via Shiprocket), EasyPost (international). Use when building shipment creation, label generation, tracking, or carrier webhook handlers. |
Shipping Carriers — DropFlow
Packages: @easypost/api (international), custom wrappers (domestic)
Location: apps/worker/src/integrations/
Carrier Selection Logic
export function selectCarrier(order: Order, shipment: ShipmentInput): ShipmentCarrier {
if (shipment.isInternational) {
return "EASYPOST_DHL";
}
return "SHIPROCKET";
}
Shiprocket Integration (Domestic India)
No official Node.js SDK — build a typed wrapper:
const BASE_URL = "https://apiv2.shiprocket.in/v1/external";
let authToken: string | null = null;
let tokenExpiry: number = 0;
async function getToken(): Promise<string> {
if (authToken && Date.now() < tokenExpiry) return authToken;
const res = await fetch(`${BASE_URL}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: env.SHIPROCKET_EMAIL,
password: env.SHIPROCKET_PASSWORD,
}),
});
const data = await res.json();
authToken = data.token;
tokenExpiry = Date.now() + 8 * 24 * 60 * 60 * 1000;
return authToken!;
}
async function shiprocketFetch(path: string, opts: RequestInit = {}) {
const token = await getToken();
const res = await fetch(`${BASE_URL}${path}`, {
...opts,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
...opts.headers,
},
});
if (!res.ok) throw new Error(`Shiprocket ${res.status}: ${await res.text()}`);
return res.json();
}
export async function createShiprocketOrder(order: Order, items: OrderItem[]) {
return shiprocketFetch("/orders/create/adhoc", {
method: "POST",
body: JSON.stringify({
order_id: order.orderNumber,
order_date: new Date().toISOString(),
billing_customer_name: order.buyerName,
billing_phone: order.buyerPhone.replace("+91", ""),
billing_address: order.billingAddress.line1,
billing_city: order.billingAddress.city,
billing_pincode: order.billingAddress.pin,
billing_state: order.billingAddress.state,
billing_country: "India",
shipping_is_billing: false,
shipping_customer_name: order.buyerName,
order_items: items.map(i => ({
name: i.product.name,
sku: i.product.sku,
units: i.quantity,
selling_price: i.unitPricePaise / 100,
hsn: i.hsnCode,
})),
payment_method: "Prepaid",
sub_total: order.subtotalPaise / 100,
}),
});
}
export async function generateAWB(shipmentId: string, courierId: number) {
return shiprocketFetch("/courier/assign/awb", {
method: "POST",
body: JSON.stringify({ shipment_id: shipmentId, courier_id: courierId }),
});
}
export async function getTracking(awbNumber: string) {
return shiprocketFetch(`/courier/track/awb/${awbNumber}`);
}
EasyPost Integration (International)
import EasyPost from "@easypost/api";
const easypost = new EasyPost(env.EASYPOST_API_KEY);
export async function createInternationalShipment(order: Order, parcel: ParcelDims) {
const shipment = await easypost.Shipment.create({
from_address: {
company: "DropFlow Seller",
street1: "...",
city: "Bangalore",
state: "KA",
zip: "560001",
country: "IN",
},
to_address: {
name: order.buyerName,
street1: order.shippingAddress.line1,
city: order.shippingAddress.city,
state: order.shippingAddress.state,
zip: order.shippingAddress.pin,
country: order.shippingAddress.,
},
: {
: parcel.,
: parcel.,
: parcel.,
: parcel. / ,
},
: {
: [{
: ,
: order.[].,
: ,
: ,
: order. / ,
: ,
}],
},
});
cheapestRate = shipment.([, , ]);
purchased = easypost..(shipment., cheapestRate);
{
: purchased.,
: purchased..,
: cheapestRate.,
: purchased..,
};
}
Carrier Webhook (app/api/v1/webhooks/carriers/route.ts)
export async function POST(req: NextRequest) {
const body = await req.json();
const { carrier, awb, status, eventTime, rawPayload } = body;
await workerClient.enqueue.mutate({
queue: "shipping-queue",
payload: {
action: "TRACKING_UPDATE",
carrier,
awbNumber: awb,
status,
eventTime,
rawPayload,
},
});
return ok({ received: true });
}
Unified Tracking Status Mapping
Map carrier-specific statuses to DropFlow's unified status:
const TRACKING_STATUS_MAP: Record<string, string> = {
"6": "IN_TRANSIT",
"7": "DELIVERED",
"8": "CANCELLED",
"in_transit": "IN_TRANSIT",
"out_for_delivery": "OUT_FOR_DELIVERY",
"delivered": "DELIVERED",
};