Features
Features for the whole order journey
Each area follows the same rules the app enforces. The tables on this page are generated from those rules, so they match the product.
Catalog and inventory
One catalog for products, variants and stock
Products, variants and SKUs live in one place. The SKU is the identity that stock is tracked against.
- Each variant (size, color and so on) has its own SKU, barcode and stock.
- Listings from TikTok Shop map to your SKUs. Price or stock differences become conflicts to review, not silent overwrites.
- Automatic price or stock sync stays off until a merchant turns it on for a shop.
- Stock can sit in warehouses, third-party logistics sites and stores, each tracked on its own.
- Reserved stock can't exceed the stock on hand. A database check enforces that rule.
Orders and fulfillment
From awaiting order to label, without double counting
Awaiting orders reserve stock. Fulfillment turns them into shipments and packages.
- Order import is idempotent. A repeated TikTok Shop message never creates a second order.
- Orders awaiting shipment reserve their stock. A cancellation releases it.
- Fulfillment creates shipments and packages, buys the label and uses the reserved stock in a single transaction.
- A shipment can contain several packages, and each package has its own tracking number.
- Labels come from the carrier adapter. Until live carrier accounts are connected, they are mock labels.
Shipment tracking
13 statuses, one history
Carrier events are normalized into 13 statuses. The current status comes from the whole history of events, not just the latest scan.
- A delivered parcel doesn't slide back to in transit when a late scan arrives.
- Returned and cancelled are final. Later scans can't reopen them.
- Text the rules don't recognize becomes Awaiting update. The system doesn't guess.
| Status in the app | Customer message | Progress bar |
|---|---|---|
| Awaiting update | We're waiting for the first update from the carrier. | Order placed |
| Label created | Your order has been packed and a shipping label has been created. | Packed |
| Pre-transit | Your parcel is being prepared for collection. | Packed |
| Shipped | Your parcel has been collected and is on its way. | Shipped |
| In transit | Your parcel is in transit. | Shipped |
| Out for delivery | Your parcel is out for delivery. | Out for delivery |
| Delivered | Your parcel has been delivered. | Delivered |
| Delayed | Your parcel is running later than expected. We're keeping an eye on it. | Shipped |
| Exception | There's a delay on your parcel. Our team is looking into it. | Shipped |
| Failed delivery | A delivery attempt was unsuccessful. The carrier will try again or arrange collection. | Shipped |
| Available for pickup | Your parcel is ready to collect from a pickup point. | Out for delivery |
| Returned | Your parcel is being returned to the sender. | Off the normal path |
| Cancelled | This shipment has been cancelled. | Off the normal path |
Delivery estimates
Business days, UK bank holidays, honest windows
Estimates start from the ship date and count business days. For GB shipments, UK bank holidays are skipped too.
| Lane | Service | Window |
|---|---|---|
| Same country | Standard | 1 to 3 business days |
| Same country | Express | 1 to 2 business days |
| Cross-border | Standard | 3 to 7 business days |
| Cross-border | Express | 2 to 4 business days |
Worked example. A same-country standard parcel shipped on Wed 23 Dec, 14:00 gets an estimate of Tue 29 Dec, with a window from Thu 24 Dec to Wed 30 Dec. UK bank holidays skipped: Fri 25 Dec and Mon 28 Dec.
Calculated by the same estimator the app uses. Basis: planning window, no history yet.
- Merchants can override the default windows in settings.
- Once there are 30 or more historical samples, the historical median replaces the default windows.
- A parcel that runs past its estimate gets a fresh estimate from today, never a date in the past.
- Estimates are planning figures, not carrier promises.
Exceptions
Problems surface before the customer asks
Exceptions are detected from each shipment's status and history. Each one has a category, a title and a detail line.
| Exception | Raised when |
|---|---|
| Running late | The estimated delivery has passed, or the carrier marks the parcel as delayed. A parcel with no carrier scan for 96 hours is flagged too. |
| Failed delivery attempt | The carrier reports a failed delivery attempt. |
| Address needs attention | The carrier's exception text mentions an address or postcode problem. |
| Held in customs | The carrier's exception text mentions customs, duty or clearance. |
| Returned to sender | The parcel is travelling back to the sender. |
| Carrier reported an exception | Any other problem the carrier reports. |
Exceptions clear automatically when the parcel is delivered or the stale condition ends.
Notifications
Email and SMS at each stage, sent once
Customers hear about each stage. Every message has a unique key, so a repeated webhook or retry can't send it twice.
| Event | Default email subject (sample values) | SMS |
|---|---|---|
| Order confirmed | Thanks for your order TT-240918-1042 | Email only |
| Order shipped | Your order TT-240918-1042 is on its way | Yes, with consent |
| In transit | Order TT-240918-1042 is moving | Email only |
| Out for delivery | Out for delivery: order TT-240918-1042 | Yes, with consent |
| Delivered | Delivered: order TT-240918-1042 | Yes, with consent |
| Delayed | An update on order TT-240918-1042 | Yes, with consent |
| Exception | An update on order TT-240918-1042 | Email only |
| Returned | Return update for order TT-240918-1042 | Email only |
The SMS for the same event reads: Northline Apparel: your order TT-240918-1042 has shipped. Track: https://track.example.com/t/sample
Template variables
{{customername}}{{ordernumber}}{{trackingnumber}}{{carrier}}{{estimateddeliverydate}}{{trackingurl}}{{storename}}{{productname}}
- Messages go out on forward status changes only. A repeated scan never sends a second message.
- SMS needs an organization switch and recorded customer consent.
- Email values are HTML-escaped. SMS messages are plain text.
- Unknown template variables are rejected when you save.
Customer tracking pages
A tracking page that shows what a shopper needs
The public page shows what a shopper needs and nothing else.
- Shows the brand, order number, items, status, estimate, carrier, tracking number, timeline and support details.
- Never shows street addresses, email addresses or phone numbers.
- Marked noindex by default. Merchants can opt in, and set a custom search title and description.
- Optional offer code, recommended products and newsletter sign-up. Newsletter sign-up records explicit consent.
- Brand and accent colors, logo, footer text and support email are set in settings. Logo links must use https.
- Visitor analytics store a daily salted hash.
Carriers
9 carriers, with honest tracking-number detection
Tracking-number detection suggests a carrier from the shape of the number. Only two formats are standardized enough to call verified: UPS 1Z numbers and Royal Mail S10 numbers ending in GB.
- DHL
- DPD
- Royal Mail
- Evri
- UPS
- FedEx
- USPS
- GLS
- Yodel
| Example number | Best match | Confidence | Format |
|---|---|---|---|
1Z999AA10123456784 |
UPS | 90% | Standard format |
AB123456789GB |
Royal Mail | 89% | Standard format |
12345678901234567890 |
USPS | 82% | Heuristic guess |
1234567890 |
DHL | 75% | Heuristic guess |
- Live carrier connections need your own carrier accounts. Until then, scans come from the mock adapter.
Team and roles
5 roles, one permission matrix
Each role gets a fixed set of permissions. The matrix below is generated from the same table the app checks.
| Area | Owner | Admin | Manager | Support | Viewer |
|---|---|---|---|---|---|
| Products | Edit | Edit | Edit | View | View |
| Inventory | Edit | Edit | Edit | View | View |
| Orders | Edit | Edit | Edit | Edit | View |
| Shipments | Edit | Edit | Edit | Edit | View |
| Notifications | Edit | Edit | Edit | Edit | View |
| Analytics | Edit | Edit | View | View | View |
| Settings | Edit | Edit | View | None | None |
| Billing | Edit | View | None | None | None |
| Integrations | Edit | Edit | View | None | None |
| Team | Edit | Edit | None | None | None |
- Owners can grant any role. Admins can grant every role except owner and admin.
- Only owners can remove another owner, and the last owner can't be removed.
- Plans limit how many team members an organization can add.
Analytics and exports
Numbers you can check
Analytics are calculated from the data behind them, not from a copied summary.
- Analytics are calculated on demand from indexed queries when you open a dashboard.
- Totals are shown per currency. Amounts are never converted silently.
- Money is stored as whole minor units, so pence and cents never drift through rounding.
- CSV exports guard against spreadsheet formula injection.
API and webhooks
Scoped API keys and signed webhooks
Integrate with the API using keys that only carry the access you give them, and receive webhooks you can verify.
API scopes
orders:readorders:syncproducts:readproducts:syncinventory:readinventory:syncshipments:readtracking:readanalytics:read
- API keys carry only the scopes they were created with, and they're stored as hashes.
- Outbound webhooks are signed with HMAC-SHA256 in the form t=timestamp,v1=signature. Receivers reject signatures older than five minutes.
- Failed deliveries retry with exponential backoff, starting at one minute. Retries wait about 1, 4, 16 and 64 minutes, and a delivery stops after 5 attempts.
- Webhook targets on private network addresses are blocked.
- Inbound webhooks are verified before anything is stored, and each provider event is stored once.
Security
Built to keep tenants apart
Customer and merchant data is treated as sensitive from the first table to the last.
- Passwords are hashed with scrypt. Sessions and verification tokens are stored as SHA-256 hashes, never as plain text.
- Webhook signing secrets and TikTok Shop tokens are encrypted with AES-256-GCM.
- Every query is filtered by organization, so one tenant can't read another tenant's data.
- Inputs are validated before any write. API responses never include buyer email addresses or phone numbers.
- Logs and audit entries are scrubbed of secrets. The audit log records who changed what, and when.
- Not built yet: multi-factor sign-in and a password change screen.
Outside accounts
What works now, and what needs an account
TrackFlow runs on labeled mock data until these connections are in place. Each one needs its own setup.
| Capability | What it needs |
|---|---|
| Live TikTok Shop sync | TikTok Shop partner approval and credentials. The mock shop stands in until then. |
| Live carrier labels and tracking | Carrier API accounts. Carrier scans come from the mock adapter until then. |
| Paid plans | Stripe prices configured. Until then, mock billing changes plans without a charge. |
| Email and SMS delivery | Provider accounts for live sending. Development prints messages to the server log. |
| Custom tracking domains | Domain verification works. Serving tracking pages from your own hostname needs edge configuration. |
| Multi-factor sign-in | Not built yet. |
| Password change screen | Not built yet. |
Mock data is labeled wherever it appears. Don't read sample orders, carrier timelines or stock levels as operational results.
See what a shopper reads.
Open the sample tracking page, switch between statuses, and see the message at each stage.