Marketplace Multi-Seller Shipping
This guide shows how to integrate Envia into a marketplace platform where multiple sellers ship orders to buyers. Your platform handles carrier selection and label generation on behalf of each seller.
What you'll build
flowchart LR Order["Buyer Places Order"] --> Route["Route to Seller"] --> Rate["Get Rates"] --> Label["Generate Label"] --> Track["Track and Notify"]
Your marketplace will:
- Let sellers fulfill orders using carriers available at their location
- Show buyers accurate shipping rates from the seller's origin
- Generate labels on behalf of sellers after purchase
- Provide unified tracking across all sellers and carriers
Architecture overview
Buyer places order (items from multiple sellers)
→ Your platform identifies each seller
→ Your backend calls Envia GET /carrier?country_code={seller.country}
→ Returns available carriers for the seller's location
→ Your backend calls Envia POST /ship/rate/ (using seller's address as origin)
→ Returns rates + delivery estimates per seller
→ Buyer selects rates and pays
→ Your backend calls Envia POST /ship/generate/ (one label per seller)
→ Returns tracking number + label PDF per seller
→ You email a unified tracking page to the buyer
→ Envia webhooks notify you of delivery status changes per shipment
Setup
All code examples below use these base URLs and token. Set ENVIA_ENV=production when you are ready to go live.
// Configuration — defaults to sandbox
const ENVIA_BASE = "https://api-test.envia.com";
const QUERIES_BASE = "https://queries-test.envia.com";
const GEOCODES_BASE = "https://geocodes.envia.com";
// Set ENVIA_TOKEN as an environment variable (see Authentication guide)# Configuration — defaults to sandbox
ENVIA_BASE = "https://api-test.envia.com"
QUERIES_BASE = "https://queries-test.envia.com"
GEOCODES_BASE = "https://geocodes.envia.com"
ENVIA_TOKEN = os.environ["ENVIA_TOKEN"]Step 1 — Discover carriers per seller location
Different sellers ship from different locations. Use the Queries API to find which carriers serve each seller's region.
async function getCarriersForSeller(seller) {
const response = await fetch(
QUERIES_BASE + "/carrier?country_code=" + seller.country,
{ headers: { Authorization: "Bearer " + process.env.ENVIA_TOKEN } }
);
return response.json();
}Step 2 — Get rates from the seller's origin
When a buyer adds a product to their cart, fetch rates using the seller's address as origin.
async function getMarketplaceRates(seller, buyerAddress, packages) {
var origin = {
name: seller.businessName,
phone: seller.phone,
street: seller.street,
city: seller.city,
state: seller.state,
country: seller.country,
postalCode: seller.postalCode,
};
var carriers = seller.preferredCarriers || ["dhl", "fedex", "estafeta"];
var ratePromises = carriers.map(function (carrier) {
return fetch(ENVIA_BASE + "/ship/rate/", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.ENVIA_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({
origin: origin,
destination: buyerAddress,
packages: packages,
shipment: { type: 1, carrier: carrier },
}),
}).then(function (r) { return r.json(); });
});
var results = await Promise.allSettled(ratePromises);
return results
.filter(function (r) { return r.status === "fulfilled" && r.value.data; })
.flatMap(function (r) { return r.value.data; })
.sort(function (a, b) { return parseFloat(a.totalPrice) - parseFloat(b.totalPrice); });
}Step 3 — Handle multi-seller orders
When a buyer purchases from multiple sellers in one order, each seller ships their items separately. Your platform generates one label per seller.
async function fulfillMultiSellerOrder(order) {
var sellerGroups = groupBySeller(order.items);
var shipments = [];
var sellerIds = Object.keys(sellerGroups);
for (var i = 0; i < sellerIds.length; i++) {
var sellerId = sellerIds[i];
var items = sellerGroups[sellerId];
var seller = await getSeller(sellerId);
var packages = buildPackages(items);
var label = await fetch(ENVIA_BASE + "/ship/generate/", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.ENVIA_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({
origin: seller.address,
destination: order.shippingAddress,
packages: packages,
shipment: {
type: 1,
carrier: order.selectedCarriers[sellerId],
service: order.selectedServices[sellerId],
},
}),
}).then(function (r) { return r.json(); });
shipments.push({
sellerId: sellerId,
trackingNumber: label.data[0].trackingNumber,
labelUrl: label.data[0].label,
});
}
return shipments;
}Step 4 — Unified tracking for buyers
Register a webhook to receive tracking updates across all carriers and sellers. Present a unified tracking page to the buyer.
curl --request POST \
--url https://queries-test.envia.com/webhooks \
--header "Authorization: Bearer $ENVIA_TOKEN" \
--header "Content-Type: application/json" \
--data '{"type_id": 3, "url": "https://your-marketplace.com/webhooks/tracking", "active": 1}'Marketplace-specific considerations
| Concern | Recommendation |
|---|---|
| Who pays for shipping? | Use a single Envia account for your marketplace. Charge sellers via your platform settlement process. |
| Seller onboarding | Collect and validate seller addresses during onboarding using the Geocodes API. |
| Carrier preferences | Let sellers set preferred carriers. Fall back to all available carriers. |
| International sellers | Use the International Shipping Workflow for cross-border orders. |
| Returns | Use the Cancel Shipment endpoint to void labels before carrier scan. Refunds are applied automatically to your account balance. |
Related pages
- Core Workflow — Full request/response examples for every shipping step
- International Shipping — Cross-border customs and HS codes
- Webhooks Guide — Real-time tracking notifications
- Production Readiness Checklist — Go-live checklist
Updated about 12 hours ago
