AstroBaaS

Orders & fulfilment

Carrier Integrations (UPS, DHL, ELTA, DPD, Speedex)

Paid pluginsize Lidea, not a commitment

Indicative price, not an offer: €99–299/month for all-5 bundle; or €20–60/month à-la-carte per carrier, plus per-label fees (€0.10–1.50 depending on service); owner sets

Generated from docs/plan/paid/carrier-integrations/PLAN.md in the project's planning notes, which are not part of the public repository. Nothing described below is implemented — it is the written plan for it.

An idea, not a commitment. It has a written plan but no place in the build order and no date. What is committed.

This plan takes over all or part of Carrier integrations (UPS, FedEx, DHL, USPS), Shipping Label Generation and Shipping Labels & Carrier Sync, entries merged into it.

A paid module providing real-time shipping rate quotes and label generation from major carriers (UPS, DHL, ELTA, DPD, Speedex). Handles credentials, carrier-specific compliance, label storage, and rate caching.

The problem

Merchants can’t offer real-time shipping rates from major carriers; they resort to manual quoting or lose customers to competitors with instant rate transparency. Building bespoke carrier API integrations is expensive and fragile across 5+ providers.

What it does

  • Support UPS, DHL, ELTA, DPD, Speedex carrier APIs (one module per carrier)
  • Accept shipment details (weight, destination, service level) and return live rate quote
  • Generate shipping labels (PDF/ZPL) and store them in artifact storage
  • Cache rates by route/weight for 24h to reduce API calls
  • Override shipping-rules-engine rates with live carrier rates (if enabled)
  • Track shipment status (in-transit, delivered, failed)
  • Support label printing (queue for printer integration or download)
  • Webhook support: receive tracking updates from carrier APIs
  • Retry logic: if carrier API is down, fall back to shipping-rules-engine
  • Per-carrier credential management (API key, account ID, password)
  • Audit trail: log every rate lookup, label generation, credential rotation
  • Support multiple carrier accounts per merchant (e.g., backup UPS account)

What it deliberately does NOT do

Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.

  • Per-shipment insurance — not included; merchant upsell. Reason: separate paid feature.
  • Customs/international compliance — core module does not validate HS codes or generate customs forms. Reason: per-country obligation, separate paid feature.
  • Carrier settlement/billing reconciliation — we don’t audit carrier invoices. Reason: financial reconciliation is outside scope.
  • Pickup scheduling — automatic scheduling of carrier pickups not included. Reason: requires carrier-specific SLA negotiation.
  • Hazmat handling — no validation of hazardous goods classification. Reason: per-country regulation, requires separate compliance module.
  • Multi-leg delivery routing — single leg only; no optimization across carriers. Reason: routing optimization is paid (advanced logistics).

Data model

New tables: CarrierAccount {id, carrierId (‘ups’|‘dhl’|‘elta’|‘dpd’|‘speedex’), merchantId, credentials: {apiKey, accountNumber, …}, isActive, rotatedAt}; ShippingLabel {id, orderId, carrierId, shipmentTrackingNumber, labelPdf (artifact ID), labelZpl, generatedAt, status (‘pending’|‘printed’|‘in_transit’|‘delivered’|‘failed’)}; RateCache {id, carrierId, origin, destination, weight, serviceLevel, rate (minor units), expiresAt}; CarrierAuditLog {id, action (‘rate_lookup’|‘label_generated’|‘tracking_updated’), carrierId, details, timestamp}. No migration.

API

  • POST /api/carriers/accounts
  • GET /api/carriers/accounts
  • DELETE /api/carriers/accounts/:accountId
  • POST /api/carriers/quote
  • POST /api/carriers/labels/generate
  • GET /api/carriers/labels/:labelId
  • POST /api/carriers/tracking
  • POST /api/carriers/webhooks/:carrierId

Every route added here must also appear in src/pages/openapi.json.ts — a test fails the build if it does not.

Admin

Carrier config page: add/remove carrier accounts, test API connection, view credential rotation history. Order fulfillment page: ‘Get Shipping Quote’ button, select carrier or auto-quote all, display rates, generate label, download/print. Label management: view generated labels, reprint, track shipment status in real-time. Audit log: all rate lookups, label generations, credential changes.

The seam — why this is paid

Core owns: shipping rules engine, order-level shipping calculation interface. Paid owns: carrier credentials, carrier API integration, real-time rate feeds, label generation, carrier-specific compliance and support.

The paid pack owns the credential (each carrier’s API key/account), per-country obligation (carrier-specific compliance and service agreements), and support commitment (handling carrier-specific bugs and rate changes).

Dependencies

  • shipping-rules-engine (used as fallback if carrier API fails)
  • Order/fulfillment context (shipment details)
  • Artifact storage (to store PDF labels)
  • Settings system (enable per-carrier, rate caching TTL)
  • Webhook system (to receive tracking updates from carriers)
  • Credential storage (secure key/value for API keys)

Acceptance checks

Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.

  • Registering a carrier account with invalid credentials returns {error: ‘invalid_credentials’} on test connection
  • Getting a rate from UPS for weight=2kg, destination=‘GR’ returns {rate: 1500, carrierName: ‘UPS’, serviceLevel: ‘ground’, eta: ‘2-3 business days’}
  • Rate cache hits: two identical rate requests within 1 hour return the same cached rate (second request does not call carrier API)
  • Rate cache misses: after 24h expiry, third request calls carrier API again
  • Generating a label for an order stores the PDF artifact and records ShippingLabel.labelPdf with artifact ID
  • Label PDF is retrievable via GET /api/carriers/labels/:labelId and returns valid PDF (magic bytes %PDF)
  • Tracking webhook from carrier updates ShippingLabel.status to ‘in_transit’ and inserts event into audit log
  • If carrier API is down (500 response), rate quote falls back to shipping-rules-engine and returns rule-based rate with {rateSource: ‘rules_engine’, carrierUnavailable: true}
  • Multiple carrier accounts for same carrier: merchant can select which account to use; system tries primary first, falls back to backup on credential error
  • Credential rotation: rotating an API key changes CarrierAccount.credentials and updates rotatedAt; old key is not used on next request

Risks

Credential leak: API keys stored in plaintext or logged; mitigation: encrypt credentials at rest, audit log shows ‘credential_rotated’ but not key. Rate quote variance: merchant quotes rate, customer waits 2h, carrier price changes; mitigation: cache TTL is transparent. Label race condition: two admins generate labels for same order simultaneously; mitigation: order-level lock. Carrier API rate-limit: 1000 requests/hour; mitigation: client-side rate-limit, queue excess.

Commercial context

Suggested price€99–299/month for all-5 bundle; or €20–60/month à-la-carte per carrier, plus per-label fees (€0.10–1.50 depending on service); owner sets
Rival anchorShopify Shipping (free for Shopify partners, €0.50–1.50/label), Pirate Ship (free USPS-only), Stamps.com (€15–100/month), ShipStation (€9.99–40/month)

The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.


Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full catalogue and CONTRIBUTING.md for how to work on this repository.