Feature spec
BeginnerCategory
payments-commerce
Build with AI
2–4 hours with Lovable or V0
Custom build
3–5 days custom dev
Running cost
$0–$10/mo up to 100 orders · $25–$100/mo at 10K orders
Works on
Everything it takes to ship Order Tracking — parts, prompts, and real costs.
Order tracking needs a Supabase orders table with a status enum and order_status_history audit trail, Supabase Realtime so the tracking page updates without a refresh, a guest lookup flow (order_number + email, no login required), and email or SMS notifications at each status change. With Lovable or V0 you can ship this in 2–4 hours for $0–$30/month at 1,000 orders. The two pieces AI tools consistently miss are notification deduplication and guest enumeration protection.
What an Order Tracking Feature Actually Is
Order tracking turns a one-way transaction into an ongoing relationship: customers know where their order is without emailing support. The core is a status timeline (Placed → Confirmed → Processing → Shipped → Delivered) that updates in real time, a tracking number that links out to the carrier, and notifications at each step. The feature has two distinct users: the logged-in customer who can see their account history, and the guest who bought without creating an account and needs to look up their order by order number and email. Supabase Realtime removes the need for polling — the tracking page updates the instant an admin or webhook changes the order status. The deceptively tricky parts are notification deduplication (one 'Shipped' email, not three) and preventing order enumeration by guessing sequential order numbers.
What users consider table stakes in 2026
- Real-time status timeline (Order Placed, Confirmed, Processing, Shipped, Out for Delivery, Delivered) with current step highlighted and no manual page refresh required
- Tracking number prominently displayed with a direct link to the carrier's tracking page (UPS, FedEx, USPS, DHL)
- Estimated delivery date shown near the top of the tracking page, not buried below the status timeline
- Email notification at each status change with the carrier name, tracking number, and a direct link to the tracking page
- Guest tracking via /track page requiring only order number and email — no account creation required
- Order summary with item names, quantities, and total displayed below the status timeline
Anatomy of the Feature
Six components build the full order tracking experience. AI tools handle the timeline UI and orders table well; the Realtime subscription reconnection, guest enumeration guard, and notification deduplication require explicit prompting.
Order status timeline
UIA vertical stepper component rendering six steps: Placed, Confirmed, Processing, Shipped, Out for Delivery, Delivered. The current status is highlighted with a filled circle; completed steps use a checkmark; future steps are greyed out. Built with a shadcn/ui custom stepper or a Framer Motion-animated progress line for visual polish.
Note: Include a Cancelled state that renders with a red indicator and shows a reason field. The cancelled branch should visually collapse the remaining steps so the UI does not show a dead path.
Orders table
DataSupabase PostgreSQL table with user_id (nullable for guests), email, order_number (unique, human-readable like 'ORD-A3F8K2'), status with a CHECK constraint on the allowed values, items (jsonb array of product name, quantity, and price), shipping_address (jsonb), tracking_number, carrier, estimated_delivery, and a notified_statuses text[] array that records which status notification emails have already been sent.
Note: The notified_statuses array is the deduplication mechanism for notification emails. Before sending any notification, check if the status is already in this array. After sending, append it via array_append in the same UPDATE statement.
Status update API
BackendAn internal PATCH /api/orders/[id]/status endpoint (or Server Action) used by admin panels, fulfillment system webhooks, or manual admin updates. Validates the status transition against an allowed transitions map before applying the update. On success, writes a row to order_status_history and triggers the notification Edge Function.
Note: Enforce status machine transitions in code: placed can only advance to confirmed or cancelled; confirmed to processing; processing to shipped; shipped to out_for_delivery; out_for_delivery to delivered. Reject invalid jumps with a 422 error.
Supabase Realtime subscription
BackendThe tracking page subscribes to a Supabase Realtime channel filtered to the specific order_id. When the status column changes — triggered by an admin update or webhook — the UI updates the active step in the timeline without a page reload. The subscription is set up as a client component using the Supabase JS SDK.
Note: Add a visibilitychange event listener that re-establishes the Realtime channel when a browser tab resumes from sleep (Chrome suspends channels after approximately 5 minutes of inactivity). Show a 'Reconnecting...' indicator during re-subscription.
Guest tracking page
UIA public /track page with a form accepting order_number and email. On submit, a Server Action or Edge Function queries orders WHERE order_number = $1 AND lower(email) = lower($2) — the email match prevents enumeration of sequential order numbers. Returns the same timeline and order summary as the logged-in view, but without auth.
Note: Rate-limit the lookup to 5 attempts per IP per hour to prevent brute-force enumeration. Store attempt counts in Supabase or a Redis-compatible store. Return a generic error ('Order not found') without specifying whether the order number or email was wrong.
Notification handler
BackendA Supabase Edge Function triggered on each status update sends a templated email via Resend (e.g., 'Your order ORD-A3F8K2 has shipped — track it here: [link]') and optionally an SMS via Twilio if a phone number is on file. Before sending, checks notified_statuses to prevent duplicate notifications. After sending, appends the status to notified_statuses using an atomic array_append UPDATE.
Note: Use Resend's idempotency key feature as a second deduplication layer on top of notified_statuses — if the Edge Function runs twice due to a retry, Resend will not deliver the email twice.
The data model
Two tables — orders and order_status_history — give you full tracking with an audit trail. Paste into the Supabase SQL editor and run before prompting your AI tool.
1create table public.orders (2 id uuid primary key default gen_random_uuid(),3 user_id uuid references auth.users(id) on delete set null, -- nullable for guest orders4 order_number text not null unique default 'ORD-' || upper(substring(gen_random_uuid()::text, 1, 8)),5 email text not null,6 status text not null default 'placed' check (7 status in ('placed','confirmed','processing','shipped','out_for_delivery','delivered','cancelled')8 ),9 items jsonb not null default '[]', -- [{name, qty, price_cents}]10 subtotal_cents int not null default 0,11 shipping_cents int not null default 0,12 total_cents int not null default 0,13 currency text not null default 'usd',14 shipping_address jsonb,15 tracking_number text,16 carrier text,17 estimated_delivery date,18 cancellation_reason text,19 notified_statuses text[] not null default '{}',20 created_at timestamptz not null default now(),21 updated_at timestamptz not null default now()22);2324alter table public.orders enable row level security;2526-- Logged-in users see their own orders27create policy "Users view own orders"28 on public.orders for select29 using (auth.uid() = user_id);3031-- Guest tracking: handled in Edge Function / Server Action — no RLS policy needed32-- because guest lookup uses service role key server-side, validates email match in code3334-- Admin update (service role bypasses RLS — call from Edge Function / Server Action)3536-- Status history for audit trail37create table public.order_status_history (38 id uuid primary key default gen_random_uuid(),39 order_id uuid references public.orders(id) on delete cascade not null,40 status text not null,41 changed_at timestamptz not null default now(),42 changed_by text -- 'system', 'admin:{user_id}', 'webhook:shipstation'43);4445alter table public.order_status_history enable row level security;4647create policy "Users view own order history"48 on public.order_status_history for select49 using (50 order_id in (51 select id from public.orders where user_id = auth.uid()52 )53 );5455create index orders_user_idx on public.orders (user_id, created_at desc);56create index orders_guest_lookup_idx on public.orders (order_number, lower(email));57create index order_history_order_idx on public.order_status_history (order_id, changed_at);5859-- Auto-update updated_at60create or replace function update_updated_at()61returns trigger language plpgsql as $$62begin63 new.updated_at = now();64 return new;65end;66$$;6768create trigger orders_updated_at69 before update on public.orders70 for each row execute function update_updated_at();Heads up: The orders_guest_lookup_idx index on (order_number, lower(email)) makes guest lookups fast while enforcing the two-field match that prevents sequential number enumeration. The notified_statuses text[] column is the deduplication guard — check it before every notification send.
Build it — pick your path
Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.
Best fit — Lovable generates Supabase schema with Realtime, the tracking timeline component, guest lookup page, and Resend email notification Edge Function in one session.
Step by step
- 1Create a new Lovable project and connect Lovable Cloud so Supabase auth and database are provisioned
- 2Paste the prompt below; Agent Mode builds the order tracking page, guest /track page, status update API, and notification Edge Function
- 3In Lovable Cloud > Secrets, add RESEND_API_KEY and optionally TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER
- 4Publish to live URL and test the Realtime subscription by opening the tracking page, then updating an order status in the Supabase Table Editor — the timeline should update within 1 second
Build an order tracking feature. Orders table: user_id (nullable for guests), email, order_number (unique, format 'ORD-' + 8 random uppercase chars), status enum (placed, confirmed, processing, shipped, out_for_delivery, delivered, cancelled), items jsonb array (name, qty, price_cents), total_cents, shipping_address jsonb, tracking_number, carrier, estimated_delivery date, notified_statuses text[] default '{}', created_at, updated_at. Also create order_status_history(id, order_id, status, changed_at, changed_by). Order tracking page at /orders/[id]: vertical stepper timeline with 6 steps (Placed, Confirmed, Processing, Shipped, Out for Delivery, Delivered) — current step highlighted, cancelled state with red indicator and reason. Show tracking number with carrier link (build a carrier URL map: UPS, FedEx, USPS, DHL, fallback Google search). Show estimated delivery date. Show items list with name, qty, and price. Subscribe to Supabase Realtime on the orders channel filtered by order_id — update stepper on status change without page refresh. Add visibilitychange listener that re-subscribes when tab becomes visible after sleep; show 'Reconnecting...' text during re-subscription. Guest tracking page at /track: form with order_number and email fields; Server Action or Edge Function queries orders WHERE order_number = $1 AND lower(email) = lower($2); rate-limit to 5 attempts per IP per hour; return generic error 'Order not found' without revealing which field was wrong. Status update API: PATCH /api/orders/[id]/status; validate transition against allowed map (placed→confirmed|cancelled, confirmed→processing, processing→shipped, shipped→out_for_delivery, out_for_delivery→delivered); insert row to order_status_history; trigger notification Edge Function. Notification Edge Function: before sending check if status already in notified_statuses array; send email via Resend with order number, status name, tracking link; after sending, UPDATE orders SET notified_statuses = array_append(notified_statuses, $status). Include 'Contact Support' CTA on tracking page. RLS: logged-in users see only own orders. Guest lookup uses service role key server-side.Where this path bites
- Supabase Realtime in the Lovable in-editor preview may lag or disconnect — test real-time updates on the published URL
- SMS via Twilio requires manual Secrets setup in Lovable Cloud after generation; the Edge Function body is scaffolded but the credentials must be added manually
- Status transition validation may need review — confirm the allowed transitions map matches your business logic before using in production
Third-party services you'll need
Supabase handles the core data and real-time layer. Resend covers email notifications. Twilio adds optional SMS. Carrier integration services are optional for automatic status pushes:
| Service | What it does | Free tier | Paid from |
|---|---|---|---|
| Supabase | PostgreSQL orders table + order_status_history, Auth, RLS, Realtime subscription for live status updates, Edge Functions for notification handler | Free (2 projects, 500MB DB) | Pro $25/mo (approx) |
| Resend | Order notification emails — 'Your order has shipped' with tracking link template; idempotency key as second deduplication layer | 100 emails/day free | $20/mo (50K emails) (approx) |
| Twilio | Optional SMS notifications at key status changes (shipped, out for delivery) for customers who provide a phone number | No free tier; $15.50 trial credit | $0.0079/SMS (US) (approx) — pay per message, no monthly fee |
| EasyPost / Shippo | Optional — carrier API integration for automatic tracking number creation and status webhook pushes (eliminates manual admin status updates) | Free tier available on both | $0.05–$0.10/shipment (approx) |
Swipe the table sideways to see pricing.
What it costs to run
Drag through the tiers to see how your monthly bill scales with users — no surprises later.
Estimated monthly running cost
Supabase free tier covers DB and Realtime easily at 100 orders. Resend free tier covers email notifications. Twilio SMS cost for 100 orders at 3 notifications each is approximately $2.40. Effectively free.
Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.
What breaks when AI tools build this
The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.
Duplicate 'Shipped' notification emails
Symptom: Stripe, fulfillment webhooks, and retry logic all fire events multiple times. If the notification Edge Function doesn't check notified_statuses before sending, a customer receives two or three 'Your order has shipped' emails within minutes — a support ticket waiting to happen.
Fix: Before sending any notification, query orders.notified_statuses and check if the status is already in the array. If present, exit early. After a successful send, execute UPDATE orders SET notified_statuses = array_append(notified_statuses, $1) WHERE id = $2 in the same database transaction. As a second layer, pass a Resend idempotency key combining order_id and status — Resend will silently discard duplicate sends with the same key.
Realtime subscription disconnects after tab sleep
Symptom: Chrome and Safari aggressively suspend background tabs after approximately 5 minutes of inactivity. When the user returns to the tracking page, the Supabase Realtime WebSocket channel has disconnected. The status may have changed while the tab was asleep, and the page shows stale data with no indication that updates were missed.
Fix: Add document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { channel.subscribe(); } }) in the client component. Simultaneously fetch the current order status via a regular query on visibility change to catch any updates that arrived while disconnected. Show a 'Reconnecting...' text during the re-subscription window.
Guest tracking allows order number enumeration
Symptom: AI tools generate the guest /track endpoint with only order_number validation. Sequential order numbers (ORD-0001, ORD-0002) can be iterated automatically — anyone can scrape all order details, emails, shipping addresses, and items from your entire order database without authentication.
Fix: Require BOTH order_number AND email match server-side before returning any data. Use a random order number format ('ORD-' + 8 random alphanumeric characters) rather than sequential integers. Add IP-based rate limiting (5 attempts per hour) and return a generic 'Order not found' message that does not confirm whether the order number or email was wrong.
Invalid status transitions allowed
Symptom: AI generates a status update API that accepts any status value and sets it directly, with no transition validation. An admin or webhook can set status to 'delivered' from 'placed', skipping 'shipped' entirely. This breaks any business logic that depends on sequential transitions (triggering packing notifications, for example) and creates audit history gaps.
Fix: Define an allowed transitions map in the status update Server Action: { placed: ['confirmed', 'cancelled'], confirmed: ['processing'], processing: ['shipped'], shipped: ['out_for_delivery'], out_for_delivery: ['delivered'] }. Reject transitions not in the map with a 422 error and a clear message: 'Cannot transition from processing to delivered — must ship first.'
Tracking link broken for unknown or domestic-only carriers
Symptom: AI tools often generate a naive tracking URL by concatenating a carrier domain string with the tracking number (carrier + '.com/track?=' + trackingNumber). This works for UPS and FedEx but breaks for USPS (uses a different query parameter), DHL, regional carriers, and any carrier with a non-standard URL structure.
Fix: Build a carrier URL map: UPS → 'https://www.ups.com/track?tracknum=', FedEx → 'https://www.fedex.com/apps/fedextrack/?trknbr=', USPS → 'https://tools.usps.com/go/TrackConfirmAction?tLabels=', DHL → 'https://www.dhl.com/en/express/tracking.html?AWB='. For unknown carriers, fall back to 'https://www.google.com/search?q=track+' + encodeURIComponent(carrier + ' ' + trackingNumber). State the carrier URL map explicitly in your prompt.
Best practices
Deduplicate notification emails at two layers: the notified_statuses array in your database and a Resend idempotency key — webhooks and retries will trigger your notification handler more than once
Enforce status transition validation in the API layer with a defined transitions map — do not rely on admin UI constraints alone, as webhooks from fulfillment systems bypass the UI entirely
Add a visibilitychange re-subscription listener to your Realtime client — without it, users returning to a sleeping tab see stale status and think tracking is broken
Always require both order_number and email for guest lookups and use a random order number format — sequential numeric IDs combined with single-field lookup are an enumeration vulnerability
Store carrier tracking links in a URL map rather than string concatenation — at least four major carriers have non-standard URL query parameter names
Use the notified_statuses text[] array rather than a separate notifications table for simple deduplication — one array column is simpler to query and update atomically
Show an estimated delivery date prominently at the top of the tracking page, not just the current status — ETA is the primary information most customers are seeking
When You Need Custom Development
Basic order tracking — status timeline, notifications, guest lookup — is comfortably in scope for Lovable or V0 in 2–4 hours. Move to custom development when tracking becomes a fulfilment system:
- Automatic tracking status updates from carrier APIs (EasyPost, ShipStation, Shippo) via webhooks — eliminating manual admin status changes entirely
- Multi-package orders where each shipment has its own tracking number and independent status timeline — requires a shipments table linked to orders
- Real-time GPS-style delivery tracking for last-mile logistics (food delivery, courier, on-demand services) — requires a live location stream, not a status enum
- White-label tracking pages for a multi-seller marketplace where each seller's customers see the seller's branding at track.sellerbrand.com
RapidDev builds these features for production
Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.
Frequently asked questions
How do I show real-time tracking updates without the user refreshing?
Use Supabase Realtime. On the tracking page, subscribe to the orders table filtered by the specific order_id using the Supabase JS SDK channel API. When your status update API or webhook changes the order status, Supabase pushes the event to all connected clients instantly. The tracking page timeline updates without any user action. Add a visibilitychange listener so the subscription re-establishes when the user returns to a sleeping browser tab.
Can guests track orders without creating an account?
Yes. Build a public /track page with a form accepting order_number and email. Your Server Action or Edge Function queries orders WHERE order_number = $1 AND lower(email) = lower($2) using the Supabase service role key (server-side only). Return the same tracking timeline and order summary as the logged-in view. Rate-limit to 5 attempts per IP per hour and return a generic 'Order not found' error without confirming which field was wrong.
How do I send SMS tracking notifications?
Add Twilio to your notification Edge Function. When an order status changes, check if the order has a phone number on file and if the customer opted into SMS. Call the Twilio Messages API with a short template: 'Your order ORD-A3F8K2 has shipped. Track it: [link]'. At approximately $0.0079/SMS (US), SMS for 1,000 shipped orders costs about $8. Include phone number as an opt-in field at checkout rather than requiring it.
What carriers can I integrate with for automatic status updates?
EasyPost and Shippo are the two services worth integrating for automatic tracking — both normalize carrier events from UPS, FedEx, USPS, DHL, and dozens of regional carriers into a consistent webhook format. When a carrier scans a package, EasyPost/Shippo sends a webhook to your PATCH /api/orders/[id]/status endpoint, updating the status automatically without admin intervention. Pricing is $0.05–$0.10 per shipment.
How do I handle orders with multiple packages?
Add a shipments table: (id, order_id, tracking_number, carrier, status, estimated_delivery). The tracking page renders one timeline per shipment rather than one per order. The order-level status advances to 'delivered' only when all shipments in that order reach delivered status — use a database trigger or Server Action that checks COUNT(*) WHERE status != 'delivered' = 0. Explicitly prompt for this table if multi-package orders are a day-one requirement.
Can I customize the tracking page with my branding?
Yes — the tracking page is a standard Next.js or Lovable page under your full control. Add your logo, brand colors, and custom copy. For white-label tracking (serving other sellers' customers under their domain), you need a custom domain setup (e.g., track.sellerbrand.com as a CNAME to your Vercel deployment) and a themes table keyed by seller_id that injects logo URL and primary color.
How do I notify customers at each status change?
Trigger a notification Edge Function from your status update API route. The function checks orders.notified_statuses to see if this status has already triggered an email; if not, it sends via Resend using a status-specific template (shipped template includes carrier and tracking link; out_for_delivery template says 'arriving today'), then appends the status to notified_statuses atomically. This ensures each status triggers exactly one email per customer.
What should I do when a shipment is delayed?
Update the estimated_delivery date on the orders row and send a proactive 'delay notification' email — a separate notification type outside the normal status flow. Store a delay_reason text field on the orders table and display it below the timeline with copy like 'Weather delay — new estimated delivery: Jan 15.' Proactive delay communication reduces support volume more than any other single change to the order tracking feature.
Need this feature production-ready?
RapidDev builds order tracking into real apps — auth, database, payments — at $13K–$25K.
Book a free consultation30-min call. No commitment.