Design a delivery workflow system where the delivery person scans products, updates states, triggers emails, and uses OTP/pictures for delivery confirmation
Let's look into FR
FR
1. Delivery Assignment
The system must assign orders to delivery personnel.
Delivery personnel should be able to view their assigned deliveries via a mobile/web app.
2. Product Scanning
Delivery personnel must scan each product (via barcode/QR) before leaving for delivery.
The system should verify that the scanned items match the order.
An error should be raised if incorrect or unassigned items are scanned.
3. State Management
Each order must move through defined states:
Pending – Order placed.
Packed – Items packed for delivery.
Out for Delivery – Delivery person scanned and picked items.
Delivered – Items successfully delivered.
Failed/Returned – Delivery could not be completed.
Transitions should be tracked and only valid transitions allowed
4. Live Tracking & Status Updates
Delivery person's location should be tracked and updated periodically.
Customers should be able to see real-time status: “Your package is 5 stops away”, etc.
5. Delivery Confirmation (OTP / Photo)
Upon delivery, the system must:
Ask the delivery person to verify the customer via OTP sent to SMS/email.
Allow a photo upload of delivered product at the doorstep (for contactless delivery or failed OTP verification).
System should store the OTP validation status and photo securely.
6. Customer Notification System
Email/SMS/push notifications must be sent to the customer at key stages:
Order packed.
Out for delivery.
Delivery attempt failed/successful.
OTP for delivery confirmation.
Optionally allow delivery reschedule in failure cases.
7. Audit Logging
Maintain an audit trail of every state change, scan, and delivery action.
Store timestamp, delivery person ID, and location for accountability.
8. Dashboard for Admins/Support
Admins should be able to:
View delivery status by area/date.
Search by order ID or delivery person.
View delivery confirmations (OTP log, photo).
Handle failed deliveries.
9. Failure Handling
If the OTP verification fails or the customer is unavailable:
System should record reason (e.g., no response, incorrect OTP).
Allow scheduling reattempt or returning the package.
Optionally notify the customer with action required.
NFR
1. Scalability
The system must support:
Up to 1 million deliveries/day.
10,000+ concurrent delivery personnel.
High write throughput for state transitions and scan events.
Must scale horizontally — for APIs, databases, and messaging queues.
2. Availability
System should have 99.9% uptime (or higher for customer-facing APIs).
Core services (OTP, scanning, status updates) must be highly available using redundant infrastructure and failover.
3. Performance
API response time (P99):
<200ms for scan and state updates.
<500ms for OTP generation/validation and image upload.
UI must show real-time delivery status with <2s latency.
4. Security
All data in transit must be TLS encrypted.
OTPs and delivery images must be securely stored (e.g., encrypted at rest).
Only authorized delivery personnel should update delivery states.
Use role-based access control (RBAC) and JWT/OAuth2 for secure APIs.
5. Reliability
Delivery workflows should be resilient to partial failures:
Retry logic for failed API calls (e.g., OTP service).
Eventual consistency using background jobs or message queues (e.g., Kafka/SQS).
Offline-first support in delivery app (with sync-on-connect logic).
6. Data Consistency
Strong consistency is required for:
OTP validation.
Delivery state transitions.
Customer-facing delivery status.
Eventual consistency acceptable for analytics and notifications.
Estimates
🧮 Calculations
1. Scans/State Transitions
Each delivery has ~3 scan events (picked, en route, delivered).
State transitions/day = 1M deliveries × ~3 = 3M writes/day
Assuming evenly spread across the day:
Writes/sec = 3M / (24×60×60) ≈ 35 writes/sec
During peak (10% in 1 hour): 300K events/hour = ~83 writes/sec
2. OTP Generation + Validation
1 OTP sent per delivery: 1M OTPs/day
Each OTP is stored + verified: 2 writes per OTP
Total OTP events/day: 2M → ~23 writes/sec (avg), ~64/sec (peak)
3. Image Upload
0.5M images/day × 200 KB = ~100 GB/day
Images stored in S3 or blob storage (cold storage)
4. Notifications
4 notifications per delivery × 1M = 4M messages/day
Push/SMS/Email ≈ ~46 notifications/sec (avg), up to 130/sec (peak)
Handled via async queue (Kafka/SQS, etc.)
API’s
📦 1. Delivery Assignment
GET /api/v1/delivery/assigned
Fetch all deliveries assigned to the logged-in delivery person.
Headers: Authorization: Bearer <token>
Response:
[
{
"delivery_id": "DEL123",
"customer_name": "Ravi",
"address": "Bangalore, India",
"status": "OutForDelivery",
"expected_delivery_time": "2025-07-01T16:30:00Z",
"items": [
{"item_id": "ITM1", "name": "Laptop", "quantity": 1},
{"item_id": "ITM2", "name": "Mouse", "quantity": 2}
]
}
]
📷 2. Product Scanning
POST /api/v1/delivery/{delivery_id}/scan
Mark an item as scanned before delivery.
Request:
{
"item_id": "ITM1",
"scanned_at": "2025-07-01T10:05:00Z"
}
🔄 3. Update Delivery Status
POST /api/v1/delivery/{delivery_id}/status
Update the delivery state (e.g., Packed → OutForDelivery → Delivered)
Request:
{
"new_status": "OutForDelivery"
}
Response: 200 OK
🔐 4. Generate OTP
POST /api/v1/delivery/{delivery_id}/otp/send
Send OTP to customer for delivery confirmation.
Response:
{
"message": "OTP sent to registered mobile number"
}
✅ 5. Confirm Delivery with OTP
POST /api/v1/delivery/{delivery_id}/otp/verify
Verify OTP for successful delivery confirmation.
Request:
{
"otp": "439872"
}
response
{
"status": "Verified",
"message": "Delivery confirmed"
}
🖼️ 6. Upload Delivery Photo
POST /api/v1/delivery/{delivery_id}/photo
Upload image as delivery proof (if OTP fails or contactless).
Form-Data:
photo
(binary image file)
Response:
{
"message": "Photo uploaded successfully",
"url": "https://cdn.deliveryapp.com/photos/del123_img.png"
}
📨 7. Notifications (Async/Internal Service)
POST /api/v1/notifications/send
Trigger notification via Email/SMS/push (internal use or from event pipeline).
Request:
{
"user_id": "USR102",
"type": "SMS",
"template": "DELIVERY_OUT_FOR_DELIVERY",
"metadata": {
"delivery_id": "DEL123",
"eta": "30 mins"
}
}
Response: 202 Accepted
🧾 8. Audit Logs (Admin/Internal)
GET /api/v1/admin/delivery/{delivery_id}/logs
Fetch full delivery lifecycle for audit purposes.
Response:
[
{
"timestamp": "2025-07-01T09:00:00Z",
"action": "SCANNED_ITEM",
"metadata": {"item_id": "ITM1"}
},
{
"timestamp": "2025-07-01T10:00:00Z",
"action": "OTP_SENT"
},
{
"timestamp": "2025-07-01T10:15:00Z",
"action": "DELIVERED",
"verified_by": "OTP"
}
]
🛑 9. Report Failed Delivery
POST /api/v1/delivery/{delivery_id}/fail
Mark delivery as failed and capture reason.
Request:
{
"reason": "Customer not available",
"attempted_at": "2025-07-01T17:00:00Z"
}
response
🔐 Auth APIs
POST /api/v1/auth/login
Login API for delivery personnel (OTP or password based)
🔄 Status Lifecycle Suggestion:
[PENDING] → [PACKED] → [OUT_FOR_DELIVERY] → [DELIVERED]
↘
[FAILED]
✅ Best Practices:
Use JWT for delivery person authentication.
Rate limit scan/photo APIs to prevent abuse.
Use event-based architecture for OTPs and notifications (decoupled from delivery flow).
Database
🧱 1. Tables Overview
| Table Name | Purpose |
| ---------------------- | --------------------------------------------- |
| `users` | Customer and delivery personnel information |
| `deliveries` | Delivery order metadata |
| `delivery_items` | Items associated with a delivery |
| `scans` | Each item scan event |
| `delivery_status_logs` | Track state transitions of deliveries |
| `delivery_photos` | Store image metadata uploaded during delivery |
| `otp_logs` | OTP generation and validation logs |
| `notifications` | Sent notification logs |
📋 2. Table Definitions
users
user_id UUID (PK)
name VARCHAR
phone VARCHAR UNIQUE
email VARCHAR
role ENUM('customer', 'delivery_agent', 'admin')
created_at TIMESTAMP
deliveries
delivery_id UUID (PK)
customer_id UUID (FK → users.user_id)
delivery_agent_id UUID (FK → users.user_id)
status ENUM('PENDING', 'PACKED', 'OUT_FOR_DELIVERY', 'DELIVERED', 'FAILED')
address TEXT
eta TIMESTAMP
created_at TIMESTAMP
updated_at TIMESTAMP
delivery_items
item_id UUID (PK)
delivery_id UUID (FK → deliveries.delivery_id)
name VARCHAR
quantity INT
scanned BOOLEAN DEFAULT false
scans
scan_id UUID (PK)
delivery_id UUID (FK)
item_id UUID (FK → delivery_items.item_id)
scanned_by UUID (FK → users.user_id)
scanned_at TIMESTAMP
status ENUM('SUCCESS', 'FAILED')
delivery_status_logs
log_id UUID (PK)
delivery_id UUID (FK → deliveries.delivery_id)
status VARCHAR -- new status
changed_by UUID (FK → users.user_id)
timestamp TIMESTAMP
note TEXT
delivery_photos
photo_id UUID (PK)
delivery_id UUID (FK → deliveries.delivery_id)
uploaded_by UUID (FK → users.user_id)
photo_url TEXT
uploaded_at TIMESTAMP
type ENUM('PROOF_OF_DELIVERY', 'FAILED_ATTEMPT')
otp_logs
otp_id UUID (PK)
delivery_id UUID (FK)
phone VARCHAR
otp_code VARCHAR
expires_at TIMESTAMP
verified BOOLEAN DEFAULT false
verified_at TIMESTAMP
created_at TIMESTAMP
notifications
notification_id UUID (PK)
delivery_id UUID (FK)
user_id UUID (FK → users.user_id)
type ENUM('EMAIL', 'SMS', 'PUSH')
template VARCHAR
status ENUM('SENT', 'FAILED')
sent_at TIMESTAMP
metadata JSONB
🧠 Design Considerations
✅ Normalization & Foreign Keys
Strong data integrity.
Easily trace delivery flow using joins (e.g., delivery → items → scan → logs).
✅ Scalability
Use UUIDs or Snowflake IDs for horizontal sharding.
Partition large tables:
delivery_status_logs
,otp_logs
,scans
, andnotifications
bydelivery_id
orcreated_at
.
✅ Storage
Photos stored in S3; only metadata in
delivery_photos
.
✅ Indexes
deliveries.status
,deliveries.delivery_agent_id
,otp_logs.delivery_id
for fast filtering.Consider GIN index on
notifications.metadata
if querying deeply.
1. Delivery assigned to agent → event: DeliveryAssigned
2. Agent scans items → event: ItemScanned
3. Agent marks as OutForDelivery → event: OutForDelivery
4. OTP generated & sent → event: OTPGenerated
5. OTP verified or photo taken → event: DeliveryConfirmed
6. Notification sent to user → event: NotificationSent
HLD
┌────────────────┐
│ Client App │
└──────┬─────────┘
│
[API Gateway]
│
┌───────────────────┼────────────────────┐
▼ ▼ ▼
[User Service] [Delivery Service] [Scan Service]
│ │
▼ ▼
[OTP Service] [Audit Log Service]
│
▼
[Notification Service]
│
▼
[Photo Service]