Warehouse and 3PL Automation
This guide shows how to integrate Envia into a warehouse or third-party logistics (3PL) operation that processes dozens to hundreds of shipments per day. The focus is on automation, batching, and efficiency.
What you'll build
flowchart LR WMS["WMS / OMS exports orders"] --> Batch["Batch Label Creation"] --> Manifest["Create Manifest"] --> Pickup["Schedule Pickup"] --> Dashboard["Tracking Dashboard"]
Your warehouse automation will:
- Process orders in batches from your WMS/OMS
- Generate labels in bulk with error handling per shipment
- Consolidate labels into manifests for carrier handoff
- Schedule daily pickups automatically
- Monitor all shipments via webhooks
Architecture overview
WMS / OMS exports pending orders
→ Your backend validates each destination address
→ Envia GET /zipcode/{country}/{code} (Geocodes API)
→ Confirms postal code and returns city/state
→ Your backend creates labels in batch (with rate limiting)
→ Envia POST /ship/generate/ (one per order)
→ Returns tracking number + label PDF per order
→ Labels grouped by carrier into manifests
→ Envia POST /ship/manifest (one manifest per carrier)
→ Daily pickup scheduled per carrier
→ Envia POST /ship/pickup/ (one per carrier per day)
→ Envia webhooks feed your tracking dashboard with status updates
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";
const WAREHOUSE_ADDRESS = { /* your warehouse origin address */ };
// Set ENVIA_TOKEN as an environment variable (see Authentication guide)# Configuration — defaults to sandbox
import os, requests
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"]
WAREHOUSE_ADDRESS = { } # your warehouse origin addressStep 1 — Batch label creation
Process orders in a loop with per-shipment error handling. Do not let one failed order block the entire batch.
async function processBatch(orders) {
var results = { success: [], failed: [] };
for (var i = 0; i < orders.length; i++) {
var order = orders[i];
try {
// Validate address first
var geoUrl = GEOCODES_BASE + "/zipcode/" + order.destination.country + "/" + order.destination.postalCode;
var geo = await fetch(geoUrl, {
headers: { Authorization: "Bearer " + process.env.ENVIA_TOKEN }
}).then(function (r) { return r.json(); });
if (!geo.data) {
results.failed.push({ order: order, error: "Invalid postal code" });
continue;
}
// Generate label
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: WAREHOUSE_ADDRESS,
destination: order.destination,
packages: order.packages,
shipment: { type: 1, carrier: order.carrier, service: order.service },
}),
}).then(function (r) { return r.json(); });
if (label.data && label.data[0] && label.data[0].trackingNumber) {
results.success.push({
orderId: order.id,
trackingNumber: label.data[0].trackingNumber,
labelUrl: label.data[0].label,
});
} else {
results.failed.push({ order: order, error: "Label creation failed" });
}
// Rate limiting: small delay between requests
await new Promise(function (resolve) { setTimeout(resolve, 150); });
} catch (err) {
results.failed.push({ order: order, error: err.message });
}
}
return results;
}Step 2 — Create a manifest
After generating labels, consolidate them into a manifest. The manifest groups all tracking numbers for a single carrier pickup.
curl --request POST \
--url https://api-test.envia.com/ship/manifest \
--header "Authorization: Bearer $ENVIA_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"carrier": "dhl",
"trackingNumbers": [
"7520610403",
"7520610404",
"7520610405"
]
}'Step 3 — Schedule a daily pickup
Schedule one pickup per carrier per day. Reference the manifest tracking numbers.
curl --request POST \
--url https://api-test.envia.com/ship/pickup/ \
--header "Authorization: Bearer $ENVIA_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"origin": {
"name": "Warehouse Operations",
"phone": "+52 8180000000",
"street": "Av. Industrial 500",
"city": "Monterrey",
"state": "NL",
"country": "MX",
"postalCode": "64000"
},
"shipment": {
"type": 1,
"carrier": "dhl",
"pickup": {
"weightUnit": "KG",
"totalWeight": 125,
"totalPackages": 45,
"date": "2026-03-04",
"timeFrom": 9,
"timeTo": 17,
"carrier": "dhl",
"trackingNumbers": ["7520610403", "7520610404", "7520610405"],
"instructions": "Loading dock B, ring bell"
}
}
}'Step 4 — Automate the daily workflow
Combine all steps into a single daily automation that runs via cron or your scheduler.
async function dailyShippingRun() {
console.log("Starting daily shipping run...");
// 1. Fetch pending orders from your WMS/OMS
var orders = await fetchPendingOrders();
console.log("Processing " + orders.length + " orders");
// 2. Create labels in batch
var results = await processBatch(orders);
console.log("Labels: " + results.success.length + " success, " + results.failed.length + " failed");
// 3. Create manifests (one per carrier)
var manifests = await createDailyManifests(results.success);
console.log("Manifests created: " + manifests.length);
// 4. Schedule pickups
for (var i = 0; i < manifests.length; i++) {
await schedulePickup(manifests[i].carrier, manifests[i].trackingNumbers);
}
console.log("Pickups scheduled");
// 5. Report failures for manual review
if (results.failed.length > 0) {
await reportFailures(results.failed);
}
return results;
}3PL-specific considerations
| Concern | Recommendation |
|---|---|
| Rate limiting | Add 100-150ms delay between API calls to avoid 429 errors. |
| Error recovery | Log failed orders for manual review. Do not block the batch. |
| Carrier cutoff times | Check pickup options via Queries API for carrier-specific cutoff times. |
| Multiple warehouses | Use different origin addresses per warehouse. Create separate manifests. |
| Balance monitoring | Monitor account balance before batch runs to prevent 402 errors mid-batch. |
| Tracking dashboard | Use webhooks to feed a real-time dashboard. See Webhooks Guide. |
Related pages
- Pickup and Manifest Workflow — Detailed pickup and manifest walkthrough
- Multiple Packages — Ship multi-box orders
- Webhooks Guide — Real-time tracking for dashboards
- Error Response Formats — How each API returns errors
- Production Readiness Checklist — Go-live checklist
Updated about 12 hours ago
