KwadMarket Docs
Product Roadmap

Delivery & Tracking

Shipment tracking, auto-validation and escrow release

After payment (escrow), the seller ships the product. We need to: let the seller enter tracking info (or generate a label), track the package automatically, auto-validate the deal when delivered, and trigger escrow release after a grace period.

Carrier integration options

ServiceCarriersFeaturesCost
Boxtal APIMondial Relay, Colissimo, Chronopost, DHL, UPSLabel generation, tracking, rate comparisonPer-shipment
SendCloud100+ carriersLabels, tracking, return portalFrom €0.40/label
17track APIUniversal trackingTracking only (no labels)Free tier: 100/day
Aftership1100+ carriersTracking onlyFree tier: 50/mo

Recommended approach

Phase 1: seller enters the tracking number manually → we track via 17track/Aftership. Phase 2: integrated label generation via Boxtal/SendCloud (seller buys shipping from the platform). Phase 1 is much simpler and covers the core need (auto-validate delivery).

Data model

model Shipment {
  id              Int      @id @default(autoincrement())
  transactionId   Int      @unique
  transaction     Transaction @relation(fields: [transactionId], references: [id])
  carrier         String?  // "colissimo", "mondial_relay", "chronopost", etc.
  trackingNumber  String
  trackingUrl     String?  // auto-generated based on carrier
  labelUrl        String?  // S3 URL of shipping label (Phase 2)
  status          ShipmentStatus @default(PENDING)
  shippedAt       DateTime?
  deliveredAt     DateTime?
  lastCheckedAt   DateTime?
  trackingHistory Json?    // array of status updates from carrier
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt
}

enum ShipmentStatus {
  PENDING          // waiting for seller to ship
  LABEL_CREATED    // label generated (Phase 2)
  SHIPPED          // tracking number entered, in transit
  IN_TRANSIT       // carrier confirms pickup
  OUT_FOR_DELIVERY // last mile
  DELIVERED        // confirmed delivery
  FAILED           // delivery failed
  RETURNED         // returned to sender
}

Tracking flow

Payment confirmed → Transaction status PAID.
Seller enters tracking number + carrier → Shipment created (SHIPPED), buyer notified, transaction → DELIVERY_PENDING.
Cron (every 2h) polls the tracking API for active shipments; status + events stored in trackingHistory.
Carrier reports DELIVERED → shipment + transaction DELIVERED, buyer notified, 48h grace period timer starts.
After the grace period with no dispute → auto-capture the Stripe payment → transaction COMPLETED, seller notified.

Carrier auto-detection

From tracking number format: 6A…/CA… → Colissimo · 1S…/3S… → Chronopost · relay-point format → Mondial Relay · 1Z… → UPS · JD… or 10 digits → DHL.

Frontend

Seller view (after sale): tracking number form, carrier dropdown (auto-detected), shipment status timeline, "Print label" (Phase 2).

Buyer view: visual status timeline, link to carrier tracking page, "I received the product" manual confirm, "Open dispute" (during grace period), ETA if available.

Tasks

Phase 1 — manual tracking: Shipment model; tracking input UI; carrier auto-detection; 17track/Aftership integration; 2h polling cron; timeline component; auto-validate (capture on delivery + grace period); shipped/delivered/released notifications.

Schema cleanup: remove canBeDelivered from Deal; remove the delivery toggle from the deal form; make location required (seller's shipping origin).

Phase 2 — label generation: Boxtal/SendCloud integration; rate comparison UI; label generation + download; automatic tracking number assignment; return labels (disputes).

Decision: no local pickup

All deals require shipping. Every transaction goes through the tracked delivery flow: proof of delivery via carrier tracking, automatic deal validation, escrow release based on real tracking data, no ambiguity about handover. This simplifies the whole payment/escrow/validation pipeline.

On this page