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
| Service | Carriers | Features | Cost |
|---|---|---|---|
| Boxtal API | Mondial Relay, Colissimo, Chronopost, DHL, UPS | Label generation, tracking, rate comparison | Per-shipment |
| SendCloud | 100+ carriers | Labels, tracking, return portal | From €0.40/label |
| 17track API | Universal tracking | Tracking only (no labels) | Free tier: 100/day |
| Aftership | 1100+ carriers | Tracking only | Free 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
Transaction status PAID.Shipment created (SHIPPED), buyer notified, transaction → DELIVERY_PENDING.trackingHistory.DELIVERED → shipment + transaction DELIVERED, buyer notified, 48h grace period timer starts.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.