medusa-shiprocket-fulfillment-sbl
v0.0.24
Published
Shiprocket Fulfillment Provider Plugin for MedusaJS 2
Maintainers
Readme
🚀 Overview
The Medusa Shiprocket Fulfillment Plugin integrates Shiprocket, India's leading logistics aggregator, directly into your Medusa store.
Streamline your shipping operations by automating rate calculations, order creation, label generation, and returns—all from within the Medusa Admin.
Compatible with Medusa v2.0+
✨ Key Features
| Feature | Description |
| :--- | :--- |
| 💵 Automated Rates | Fetch real-time shipping rates at checkout based on pickup and delivery pin codes. Automatically selects the cheapest available courier. |
| 📦 Seamless Fulfillment | Automatically create shipments in Shiprocket when you fulfill an order in Medusa. Includes automatic AWB (Air Waybill) assignment. |
| 📄 Document Generation | Automatically generate and retrieve Shipping Labels, Manifests, and Invoices directly from Shiprocket. Documents are accessible via the admin widget. |
| 🖥️ Admin Widget | Beautiful admin widget on order details page showing shipment status, tracking info, and quick access to labels, manifests, and invoices. |
| ↩️ Returns Management | Handle return requests and generate reverse pickup shipments effortlessly with dedicated return fulfillment support. |
| 🔍 Serviceability & ETD | GET /store/shiprocket/serviceability checks pincode serviceability and EDD using the variant’s weight/dimensions and pickup from Medusa stock location addresses (inventory levels), with optional pickup_pincode override—no cart required. |
| 🛑 Easy Cancellation | Cancel shipments instantly from the Medusa Admin to void labels and prevent unnecessary charges. |
| 🔐 Auto Token Refresh | Scheduled job automatically refreshes Shiprocket authentication tokens every 8 days to ensure uninterrupted service. |
| 📊 Tracking Support | Built-in tracking number and tracking URL integration for real-time shipment status updates. |
| 🔔 Status Webhook | Optional webhook for Shiprocket to notify when an order is shipped or delivered; automatically creates shipment or marks fulfillment as delivered in Medusa. |
| 🧾 Tracking event stream | Optional shipment_tracking module (bundled with this plugin) stores Shiprocket scan history (append-only, deduped); webhook ingest and admin APIs use it and emit shipment.tracking.updated when the latest normalized milestone changes. |
| 🇮🇳 India-First | Optimized for Indian addresses, GST compliance, HSN codes, and domestic courier networks. |
📋 Prerequisites
Before you begin, ensure you have:
- A Medusa v2 server set up.
- A Shiprocket account.
- At least one Pickup Location (nickname) in your Shiprocket dashboard—used when creating shipments from Medusa fulfillments (must match
pickup_location/SHIPROCKET_PICKUP_LOCATIONin the fulfillment providermodulesconfig). - For checkout rates and store serviceability, Stock locations in Medusa need a valid address with postal code, and variants need inventory with location levels linked to those locations (see Product Requirements).
🛠️ Installation
Install the plugin using your preferred package manager:
npm install medusa-shiprocket-fulfillment-sbl
# or
yarn add medusa-shiprocket-fulfillment-sbl⚙️ Configuration
1. Environment Variables
Add your Shiprocket credentials to your .env file.
[!WARNING] Security Note: Never commit your actual API passwords to version control (git).
SHIPROCKET_EMAIL="[email protected]"
SHIPROCKET_PASSWORD="your_shiprocket_password"
# Must match the 'Nickname' of a pickup location in your Shiprocket dashboard (fulfillment / order creation).
SHIPROCKET_PICKUP_LOCATION="Primary"2. Medusa Config
Register the plugin in your medusa-config.js (or medusa-config.ts) file. You need to add it to the modules section for the fulfillment provider.
module.exports = defineConfig({
// ... other config
modules: [
{
resolve: "@medusajs/medusa/fulfillment",
options: {
providers: [
{
resolve: "medusa-shiprocket-fulfillment-sbl",
id: "shiprocket",
options: {
email: process.env.SHIPROCKET_EMAIL,
password: process.env.SHIPROCKET_PASSWORD,
pickup_location: process.env.SHIPROCKET_PICKUP_LOCATION || "Primary",
/**
* Cash on Delivery support.
* Accepts: 0, 1, "true", or "false"
* Set to 1 or "true" to enable COD for rate calculations.
*/
cod: "false",
},
},
],
},
},
],
plugins: [
{
resolve: "medusa-shiprocket-fulfillment-sbl",
options: {
// Required Shiprocket configuration
shiprocket: {
email: process.env.SHIPROCKET_EMAIL,
password: process.env.SHIPROCKET_PASSWORD,
},
},
},
],
});Required Plugin Options:
shiprocket.email- Your Shiprocket account email (required)shiprocket.password- Your Shiprocket account password (required)shiprocket.pickupLocation(optional) — Shiprocket warehouse nickname; defaults to"Primary"if omitted. Used where the plugin talks to Shiprocket with a warehouse name (e.g. admin document flows); the fulfillment provider usespickup_locationin themodulesconfig above.
Optional (for webhook):
webhookSecret- Shared secret for Shiprocket status webhook. If set, Shiprocket can notify your store when an order is shipped or delivered. UsePOST /carrier-tracking/webhookin Shiprocket’s dashboard.publishableKey- Medusa publishable API key (optional). Resolved in the webhook handler if you add extra checks; not required forPOST /carrier-tracking/webhook.
[!NOTE] The admin widget is automatically included when the plugin is installed. No additional plugin registration is required for the admin UI.
3. Automatic Token Refresh
The plugin includes a scheduled job that automatically refreshes Shiprocket authentication tokens every 8 days to ensure uninterrupted service. This job runs automatically and requires no configuration.
- Schedule: Every 8 days at 00:00 (midnight)
- Job Name:
refresh-shiprocket-token - Automatic: No manual intervention required
The plugin also handles token refresh automatically when API requests receive 401 Unauthorized responses, ensuring seamless operation even if the scheduled job is delayed.
🖥️ Admin Widget
The plugin includes a built-in admin widget that appears on the order details page, providing quick access to Shiprocket documents and shipment information.
Features
- Shipment Status Display: Shows the current status of each active shipment (Delivered, Pending, Cancelled) with color-coded badges
- Quick Document Access: Direct links to download:
- Shipping Label: Print-ready shipping label for the shipment
- Manifest: Shipping manifest document
- Invoice: Order invoice from Shiprocket
- Tracking Information: Displays Shipment ID and tracking details
Location
The widget automatically appears in the Order Details page sidebar (order.details.side.after zone) for orders with active Shiprocket fulfillments.
[!NOTE] The widget only displays for fulfillments that have been successfully created in Shiprocket (with AWB and shipment_id). Cancelled or failed fulfillments are not shown.
🌐 Storefront API
The plugin provides a custom storefront endpoint for quick pre-checkout serviceability and delivery estimates.
Check Serviceability
GET /store/shiprocket/serviceability
Use this endpoint on product pages or in the cart to check if a pincode is serviceable and get estimated delivery dates.
- Dimensions & weight come from the variant (Medusa: weight in grams, length/width/height in cm).
- Pickup postcode is chosen from inventory location levels → stock location → address.postal_code (same idea as checkout pickup from stock).
pickup_pincode(optional query param) overrides the above for testing or special cases.
Query Parameters
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| pincode | string | Yes | The delivery pincode to check. |
| variant_id | string | Yes | Product variant id. Must have weight (g), length, width, and height (cm) set. |
| pickup_pincode | string | No | Overrides pickup postcode (skips stock location resolution). |
| cod | integer | No | 1 for COD, 0 for Prepaid. Defaults to 0. |
| declared_value | integer | No | Declared shipment value in INR. |
| order_id | integer | No | Existing Shiprocket order id, if applicable. |
Example Request
Store routes typically require Medusa’s publishable key header (adjust host and keys for your project):
curl -X GET "http://localhost:9000/store/shiprocket/serviceability?pincode=110001&variant_id=variant_01HQWE..." \
-H "x-publishable-api-key: YOUR_PUBLISHABLE_KEY"Optional: &pickup_pincode=560001 to force a pickup pincode instead of resolving it from stock locations.
Example Response
{
"status": 200,
"data": {
"available_courier_companies": [
{
"id": 1,
"courier_name": "Delhivery",
"rate": "120.00",
"etd": "2026-01-15",
"estimated_delivery_days": 3
}
]
}
}Shiprocket Status Webhook
| Endpoint | Use case |
| :--- | :--- |
| POST /carrier-tracking/webhook | Shiprocket dashboard URL — Medusa does not require x-publishable-api-key here. Path avoids Shiprocket’s banned substrings (shiprocket, kartrocket, sr, kr). |
Allows Shiprocket to notify your store when an order is shipped or delivered. The plugin then automatically updates the Medusa order (creates shipment or marks fulfillment as delivered), so you no longer need to do this manually in the admin.
Configuration
- Webhook secret: In plugin options, set
webhookSecret(e.g. fromprocess.env.SHIPROCKET_WEBHOOK_SECRET). If not set, the webhook responds with 501 and does not process requests. - Publishable key (optional): Set
publishableKeyin plugin options only if you extend the handler to validate it; Shiprocket does not send this header. - Shiprocket dashboard: In Shiprocket, go to Settings → Additional Settings → Webhooks. Enable the webhook, set URL to
https://your-medusa-backend.com/carrier-tracking/webhook, and set Token to the same value as yourwebhookSecret. Shiprocket sends the Token in thex-api-keyHTTP header. - Authentication: The plugin accepts the secret via any of:
- Header:
X-Api-Key: <webhookSecret>(used by Shiprocket when you set the Token in their dashboard) - Header:
Authorization: Bearer <webhookSecret> - Header:
X-Shiprocket-Secret: <webhookSecret> - Query:
?secret=<webhookSecret>(less secure; use only if Shiprocket cannot send headers)
- Header:
Example plugin config (with webhook)
plugins: [
{
resolve: "medusa-shiprocket-fulfillment-sbl",
options: {
shiprocket: {
email: process.env.SHIPROCKET_EMAIL,
password: process.env.SHIPROCKET_PASSWORD,
},
webhookSecret: process.env.SHIPROCKET_WEBHOOK_SECRET,
publishableKey: process.env.MEDUSA_PUBLISHABLE_KEY,
},
},
],Expected webhook body (Shiprocket)
The handler accepts a flexible JSON body. Typical fields:
| Field | Type | Description |
| :--- | :--- | :--- |
| order_id | string/number | Shiprocket order id |
| shipment_id | string/number | Shiprocket shipment id |
| channel_order_id | string | Medusa order id (order_...) or numeric display_id (e.g. 1001); the plugin sends display_id to Shiprocket when present so the dashboard shows a short order number |
| status, shipment_status, or current_status | string | e.g. "shipped", "Delivered" (normalized in code) |
| tracking_number, tracking_url, awb_code | string | Optional; used when creating the shipment |
| awb | number/string | AWB / tracking; used for lookup and labels. If it matches fulfillment.data, a Manual fulfillment can still be matched |
At least one of order_id, shipment_id, awb, or channel_order_id is required. Status is read from status, then shipment_status, then current_status. Values like "shipped", "delivered", "Delivered", "dispatch", "delivery" are normalized automatically.
When the status is delivered and Medusa has not shipped the fulfillment yet, the handler runs create shipment (admin “Mark as shipped”) using AWB/URLs from the payload, then mark as delivered (“Mark as delivered”).
Shiprocket may send additional fields (e.g. scans, current_timestamp, etd, channel, courier_name); these are accepted. When the shipment_tracking module is registered (see below), webhook payloads with scans (or a normalizable top-level status) are ingested into an append-only event table with deduplication (dedupe_hash from AWB + normalized activity + event time). Ingestion failures are logged; the handler still tends to respond 200 so Shiprocket does not retry aggressively.
Fulfillment timestamps: Medusa core Fulfillment does not expose packed_at in all versions. Packed milestones are represented in the tracking stream (and customer timeline), not written to a core packed_at field unless you add your own metadata convention.
The exact payload from Shiprocket may differ; confirm from Shiprocket’s dashboard or docs and adjust the parser if needed.
Query parameters
| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| shiprocket_only | string | true | When true or omitted, only Shiprocket provider fulfillments are matched unless the payload AWB matches fulfillment.data (so Manual fulfillments with stored AWB can still match). When false, any fulfillment matching ids/AWB is used. |
Example request
curl -X POST "http://localhost:9000/carrier-tracking/webhook" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_WEBHOOK_SECRET" \
-d '{"order_id": "12345", "shipment_id": "67890", "status": "delivered"}'Responses
- 200: Status processed (shipment created and/or marked delivered), ignored (e.g. status not shipped/delivered), idempotent (already shipped/delivered), or no fulfillment matched (dummy test ids — still
200). - 400: Invalid body (e.g. missing identifiers or invalid shape).
- 401: Missing or invalid webhook secret.
- 501: Webhook not configured (no
webhookSecretin plugin options).
Shipment tracking module (optional)
Register the shipment_tracking module from this package (migration and service live under medusa-shiprocket-fulfillment-sbl/modules/shipment-tracking). No separate plugin is required.
module.exports = defineConfig({
// ...
modules: [
// ...existing modules
{
resolve: "medusa-shiprocket-fulfillment-sbl/modules/shipment-tracking",
},
],
});After upgrading, run DB migrations (same as your Medusa workflow: npx medusa db:migrate or plugin migration command) so table shipment_tracking_event exists.
Do not register this module twice (for example alongside
order-management/modules/shipment-tracking) — both use the same module keyshipment_trackingand the same table.
Identifiers: Webhook order_id / shipment_id are Shiprocket-side ids. channel_order_id echoes what you sent at create time: numeric Medusa display_id (preferred for a short Shiprocket UI label) or internal id (order_...). The webhook resolves both. awb (or awb_code / tracking_number) ties scans to fulfillment lookup and dedupe hashing.
Deduping: Each scan row is keyed by dedupe_hash (SHA-256 of AWB + normalized activity text + raw event time string). Replays of the same scan do not insert duplicate rows.
Internal event: When ingestion changes the latest normalized milestone for a fulfillment, the webhook emits shipment.tracking.updated on the Medusa event bus with payload shape:
{
"order_id": "order_...",
"fulfillment_id": "ful_...",
"latest_normalized": "delivered",
"previous_normalized": "in_transit"
}Read APIs
| Scope | Method | Path | Notes |
| :--- | :--- | :--- | :--- |
| Store | GET | /store/orders/:order_id/shipment-timeline | Not part of this package. You can expose timeline data with a custom route using shipment_tracking, or use order-management if you already depend on it. |
| Admin | GET | /admin/orders/:id/shipment-tracking-events | Full rows including raw_payload (admin auth). |
If the module is not registered, webhook tracking ingest and GET /admin/orders/:id/shipment-tracking-events respond 501 with a hint to add the module.
[!NOTE] The public webhook path remains
POST /carrier-tracking/webhook(not/webhooks/shiprocket) so Shiprocket’s URL validation accepts it.
💻 Usage Guide
Enabling the Provider
- Log in to your Medusa Admin.
- Go to Settings → Regions.
- Select the region you want to ship to (e.g., "India").
- In the Fulfillment Providers section, edit and ensure
shiprocketis selected. - Save changes.
Shipping Options
You can now create Shipping Options (e.g., "Standard Shipping") that use the shiprocket provider.
- Calculated: Choose "Calculated" price type to use Shiprocket's real-time rate API, which automatically selects the cheapest available courier.
Product Requirements
For successful fulfillment creation, ensure your product variants have the following properties set:
- Weight (in grams): Required for rate calculation and shipment creation
- Length (in cm): Required for shipment creation
- Width (in cm): Required for shipment creation
- Height (in cm): Required for shipment creation
- HSN Code: Optional but recommended for GST compliance
For GET /store/shiprocket/serviceability (in addition to the dimensions above):
- The variant must have inventory linked (Manage inventory in Admin when applicable) with at least one inventory level on a stock location whose address includes a postal code (Indian pincode). The endpoint picks a level preferring positive available quantity, then highest quantity; if none have stock, any level with a postal code is used.
- If the variant cannot resolve a pickup postcode, pass
pickup_pincodeon the query string or fix stock location / inventory in Admin.
[!WARNING] Missing dimensions or weight will cause fulfillment creation to fail with an error. Ensure all product variants have these values configured in the Medusa Admin.
Creating a Fulfillment (Shipment)
When you fulfill an order in the Medusa Admin:
- The plugin creates an order in Shiprocket with all required shipping and billing information. It sends Medusa
display_idasorder_idandchannel_order_idwhen the order has a positive numeric display id (short label in Shiprocket); otherwise it falls back to the internalorder_...id. Webhooks match either form. - It automatically assigns an AWB (Air Waybill) using Shiprocket's "adhoc" API.
- If successful, the Tracking Number and Tracking URL are saved to the fulfillment in Medusa.
- Documents are automatically generated: Shipping label, manifest, and invoice are created and linked to the fulfillment.
- The Admin Widget on the order details page will display the shipment status and provide quick access to all documents.
Viewing Shipment Documents
After fulfillment creation, you can access documents in two ways:
- Admin Widget (Recommended): Navigate to the order details page in Medusa Admin. The Shiprocket Printables widget in the sidebar shows direct links to Label, Manifest, and Invoice.
- Fulfillment Data: Documents URLs are stored in the fulfillment's
labelsproperty and can be accessed programmatically.
🐛 Troubleshooting
Store serviceability (GET /store/shiprocket/serviceability)
- Missing inventory / stock location data: Ensure the variant has inventory levels tied to a stock location with a postal code on the address (see Product Requirements).
- Missing variant dimensions: Set weight (grams) and length, width, height (cm) on the variant; the endpoint returns
400if they are missing or invalid. - Bypass pickup resolution: Add
pickup_pincodeto the query string when you only need a quick Shiprocket check against a known warehouse pincode.
Rate Calculation Issues
"Both pickup and delivery postcodes are required for rate calculation"
- Ensure your Stock Location has a valid address with a 6-digit Indian postal code configured.
- Verify the customer's shipping address has a valid 6-digit Indian postal code.
- The pickup postcode comes from your Stock Location address linked to the Sales Channel.
"No couriers available for this route"
- The route between pickup and delivery pincodes may not be serviceable by any courier.
- Verify both pincodes are valid and serviceable in Shiprocket's network.
- Check if there are any serviceability restrictions in your Shiprocket dashboard.
"Rate calculation failed unexpectedly"
- Check that product variants have
weightset (in grams). If weight is missing, the plugin defaults to 0.5kg, but this may cause issues. - Verify network connectivity to Shiprocket API.
- Check plugin logs for detailed error messages.
Fulfillment Creation Issues
"Missing dimensions/weight for item [item name]. Please update product variant settings."
- Required: Each product variant must have:
weight(in grams)length(in cm)width(in cm)height(in cm)
- Update product variants in Medusa Admin → Products → [Product] → Variants → Edit
- All dimensions are required for Shiprocket to create the shipment.
"Failed to create Shiprocket order: No shipment ID returned"
- This indicates Shiprocket API returned an unexpected response.
- Check Shiprocket dashboard for any account restrictions or issues.
- Verify all required order data is present (billing/shipping addresses, items, etc.).
"AWB assignment failed"
- The order was created but AWB assignment failed. The plugin attempts to cancel the order automatically.
- Check Shiprocket dashboard for courier availability issues.
- Verify your Shiprocket pickup location (nickname /
SHIPROCKET_PICKUP_LOCATION) is active and correctly configured.
"Shiprocket Error: [error message]"
- This is a direct error from Shiprocket API. The message will indicate the specific issue.
- Common causes:
- Invalid address data
- Missing required fields
- Account restrictions
- API rate limits
Authentication Issues
"Shiprocket API credentials are required"
- Ensure
SHIPROCKET_EMAILandSHIPROCKET_PASSWORDare set in your.envfile. - Verify credentials are correct and your Shiprocket account is active.
Token Refresh Failures
- The plugin automatically refreshes tokens every 8 days via a scheduled job.
- If authentication fails, check:
- Credentials are still valid
- Account is not suspended
- Network connectivity to Shiprocket API
Pickup Location Issues
"Pickup location not found" (Shiprocket order / fulfillment)
This applies when creating a shipment in Shiprocket using the warehouse nickname from your fulfillment provider config (pickup_location / SHIPROCKET_PICKUP_LOCATION).
- Ensure that value matches the exact Nickname of a pickup location in your Shiprocket dashboard.
- The pickup location must be active and properly configured in Shiprocket.
- Default value is
"Primary"if not specified.
Store GET /store/shiprocket/serviceability — pickup postcode
- Default pickup comes from Medusa: variant inventory → location levels → stock location → address.postal_code.
- Errors about no inventory linked or no postal code: enable/link inventory for the variant, ensure location levels exist, and set the stock location address in Medusa Admin—or pass
pickup_pincodeon the request.
Admin Widget Not Showing
- The widget only appears for fulfillments with:
awb(Air Waybill) assignedshipment_idpresent- Provider ID is
shiprocket - Fulfillment is not cancelled
- If documents are missing, the fulfillment may have been created but document generation failed. Check fulfillment data for error messages.
🤝 Contributing
Contributions are welcome! If you find a bug or want to add a feature:
- Fork the repository.
- Create a feature branch (
git checkout -b feature/amazing-feature). - Commit your changes.
- Open a Pull Request.
📄 License
This project is licensed under the MIT License.
