Draft Orders
Staging buffer between message-sourced orders (AI parser, catalog picks) and confirmed
Orderrows. Human-in-the-loop review + edit before anOrderis created.
1. Overview
Orders from WhatsApp/Instagram messages, the AI parser, or a catalog quick-pick are not inserted directly into Order. They land in DraftOrder with status pending_review. A seller reviews, edits, and either approves (→ Order) or rejects (→ archived). Drafts are the unit of human control for message-driven commerce.
2. Architecture
3. Data model
See schema.prisma:L478-L539 for DraftOrder, DraftOrderItem, and DraftOrderStatus enum (pending_review | approved | rejected | expired).
4. Key flows
4.1 Approval → Order (single transaction)
The eager status flip to approved before the Order insert is the guard against duplicate conversion on concurrent approves.
4.2 Creation from a message
5. Lifecycle
All terminal. No reopening.
6. Key files
- backend/lib/draft-order-service.ts —
createDraftOrder,approveDraftOrder,cleanupExpiredDrafts - backend/routes/draft-orders.ts — CRUD + approve/reject; L510-L545 hosts
GET /pending-test(unauthenticated debug, hardcoded userId — remove before prod) - backend/lib/order-creation-service.ts —
createDraftOrderFromMessage - backend/lib/whatsapp-catalog-order-processor.ts:L100
- backend/lib/instagram-dm-processor.ts:L202
- backend/routes/whatsapp-business-webhook.ts:L536, meta-webhooks.ts:L889 — local helper variants
- src/components/DraftOrdersDashboard.tsx — review UI
The race-condition fix (tx + status flip) landed in commit 68bbf56 ("Added draft-order fixes").
7. Env vars & config
No dedicated env vars. Inherits DB, JWT, and WhatsApp credentials.
8. Gotchas & troubleshooting
expiresAtis passive → set tonow + 24hbut no scheduler callscleanupExpiredDrafts. Only trigger is manualPOST /cleanup/expired. Drafts pile up forever.- Unauthenticated debug endpoint →
GET /pending-testat draft-orders.ts:L510-L545 has no auth, hardcodeduserId. Remove before prod exposure. customeris denormalized JSON → not an FK toCustomer. Editing the Customer record won't retroactively update drafts.DraftOrderItem.productIdmay be null → happens when parser can't match. Approval must fill these in or the Order insert fails.- PATCH/PUT recompute sets
totalAmount = subtotal→ tax and shipping ignored on edit. Real money bug if drafts with tax are edited post-parse. - WhatsApp confirm on approve is best-effort → try/catch + swallow. Failures silent; Order still created. Check BE logs if sellers report missing confirmations.
- Five helpers create drafts → canonical
createDraftOrderplus four local helpers in webhook files. Logic drift is real — route new code throughOrderCreationService.createDraftOrderFromMessage.
9. Extension points
- New draft source → call
OrderCreationService.createDraftOrderFromMessage(parsed). Don't inline a new helper. - Auto-approve for trusted customers → rule layer before
createDraftOrder; on match callapproveDraftOrderimmediately. - Expiry scheduler → add a cron in schedulers/ calling
cleanupExpiredDraftsdaily.