Product Submissions¶
Table of Contents
- Overview
- Workflow at a Glance
- Customer Workflow
- Staff (Admin) Workflow
- API Reference
- Notifications
- Audit Trail
- Troubleshooting
- Related Documentation
Overview¶
The Product Submissions feature lets customers propose new products for the catalog through the customer portal. Submissions are queued for staff review: approved submissions become first-class Product records, while rejected submissions return to the customer with a reason.
This gives customers a self-service path to expand the catalog without giving them direct write access, and gives staff an approval gate to enforce SKU, dimensional, and handling-class standards before a product enters the system.
Key Capabilities:
- Customer-initiated submission form with inline SKU-availability checking
- Three-state lifecycle: Pending, Approved, Rejected
- Approve action atomically creates the catalog Product and relinks any pending delivery-request lines
- Reject action captures a required reason that is shown back to the customer
- In-app notifications on create / approve / reject for both audiences
- Full audit trail via the platform audit module
Who can submit?
Only portal users linked to a customer account and holding the products:submit permission may create submissions. Internal staff without a customer link cannot submit — they review.
Who can approve or reject?
Only users with the products:manage permission see the Approve and Reject buttons. Viewing the queue requires products:view.
Workflow at a Glance¶
graph LR
A[Customer fills<br/>Submit Product form] --> B[SKU availability<br/>check]
B --> C[Submission<br/>PENDING]
C --> D{Staff<br/>review}
D -->|Approve| E[Catalog Product<br/>created]
D -->|Reject| F[Rejection reason<br/>captured]
E --> G[Customer notified<br/>APPROVED]
F --> H[Customer notified<br/>REJECTED]
style A fill:#e3f2fd
style C fill:#fff3e0
style D fill:#fff3e0
style E fill:#e8f5e9
style F fill:#ffebee
style G fill:#e8f5e9
style H fill:#ffebee Customer Workflow¶
This section is for customer-portal users submitting products for catalog inclusion.
Accessing the Submission Form¶
- Sign in to the Customer Portal
- Open the Products page from the sidebar
- Click Submit Product in the top-right corner
- The Submit Product modal opens
Delivery request shortcut
If you are filling out a delivery request and the product you need is not in the catalog, the Create Delivery Request flow includes a shortcut that opens this same submission form and automatically links the new submission to the in-progress delivery request. Once the submission is approved, the delivery-request line is re-pointed to the real product with no further action needed from you.
Required and Optional Fields¶
| Field | Required | Notes |
|---|---|---|
| Product Name | Yes | Up to 255 characters. Example: Heavy Duty Widget |
| SKU | Yes | Up to 100 characters. Letters, numbers, and hyphens only. Checked live for availability. |
| Unit of Measure | Yes | One of: EACH, CASE, PALLET, LB, KG, OZ, G |
| Description | No | Up to 2,000 characters. Free-form product description. |
| Weight | No | In pounds. Decimal allowed. |
| Length / Width / Height | No | In inches. Decimal allowed. |
| Cases per Pallet | No | Whole number, 1 – 9,999. |
| Notes | No | Up to 2,000 characters. Special handling, compliance, or any info for the reviewer. |
Provide dimensions and weight when possible
Reviewers use weight and dimensional data to assign storage and freight classes during approval. Submissions missing this data are approvable but may be sent back for revision if your operations team needs it upfront.
SKU Availability¶
As you type the SKU, the form debounces and calls the availability endpoint. You will see one of three states:
- Available — green check, safe to proceed
- Already in the catalog — the SKU belongs to an existing approved product
- Pending approval — another submission (possibly your own) is already using this SKU
If the SKU is taken you must change it before you can submit.
Submitting¶
- Review all fields
- Click Submit for Review
- A success toast confirms the submission; the modal closes
The submission is created in PENDING status. Both staff reviewers and your own customer account receive an in-app notification.
Checking Submission Status¶
- Navigate to Products in the portal
- Open the My Submissions tab (or click the notification link)
- The table shows every submission you have made, scoped automatically to your customer account
The Status column badge is color-coded:
| Badge | Meaning |
|---|---|
| Pending | Waiting for staff review |
| Approved | Accepted — a catalog product has been created |
| Rejected | Declined — open the row to see the rejection reason |
Clicking a row opens the detail panel, which includes the fields you submitted, the review outcome, the reviewer's name, and the review timestamp. Rejected rows display the full Rejection Reason so you can correct and resubmit.
Staff (Admin) Workflow¶
This section is for internal staff with the products:manage permission reviewing the submission queue.
Reviewing the Submission Queue¶
Navigate to Product Submissions from the main sidebar. The page lives at /product-submissions and defaults to the Pending filter so you see the active queue first.
Interface elements:
- Search bar — search by product name or SKU
- Status filter —
All Statuses,Pending,Approved,Rejected(default:Pending) - Submissions table — one row per submission
- Pagination — 20 rows per page by default
- Row click — opens the Review modal
Table columns:
| Column | Description |
|---|---|
| Product Name | Submitted product name |
| SKU | Proposed SKU |
| Customer | Submitting customer's company name |
| Submitted By | Portal user who submitted |
| Status | Color-coded status badge |
| Submitted | Relative timestamp (e.g., "2 hours ago") |
Opening a Submission for Review¶
Click any row to open the Review modal. The modal shows every field the customer entered:
- Identification: name, SKU, status badge
- Logistics data: unit of measure, weight, length × width × height, cases per pallet
- Description and Notes (if provided)
- Submission metadata: customer, submitted-by user, submission timestamp
- For reviewed submissions: Reviewed By, Reviewed At, and Rejection Reason (rejected only) or Linked Product (approved only)
Approving a Submission¶
Approving a submission creates a real Product in the catalog and links back to the submission.
- Open the submission in the Review modal
-
(Optional) Expand Show Overrides to adjust any of the following before creating the product:
Override Purpose SKU Normalize the SKU to your internal scheme Name Tidy up the display name Storage Class Assign a storage class (e.g., AMBIENT,REFRIGERATED)Freight Class Assign a freight class for rating NMFC Code National Motor Freight Classification code Lot Tracked Enable lot tracking on the new product Serial Tracked Enable serial-number tracking Reorder Point / Reorder Quantity / Max Stock Inventory planning defaults Cost / Price Financial defaults -
Click Approve
What the system does on approve (wrapped in a single transaction):
- Creates a new
Productentity using the submission's fields plus any overrides - Sets the submission
statustoAPPROVED, stampsreviewedAtandreviewedByUserId, and stores the newproductId - Re-points any pending
DeliveryRequestLinerows that referenced this submission so they now reference the real product - Emits
product-submission.approved, which sends the customer an in-app notification - Writes a
STATUS_CHANGEentry to the audit log
SKU conflicts at approval time
If another product takes the same SKU between submission and approval (a TOCTOU race), the approve action fails with a conflict error. Use the SKU override field to assign a different SKU and try again.
Rejecting a Submission¶
- Open the submission in the Review modal
- Scroll to the Reject section
- Enter a Rejection Reason (required — the API rejects empty reasons with a 400)
- Click Reject
What the system does on reject:
- Sets the submission
statustoREJECTED, stampsreviewedAtandreviewedByUserId, and stores therejectionReason - Collects any delivery-request lines that referenced this submission so the customer knows which requests are blocked
- Emits
product-submission.rejected, which sends the customer an in-app notification containing the rejection reason - Writes a
STATUS_CHANGEentry to the audit log
Write useful rejection reasons
The rejection reason is shown verbatim to the customer. Be specific and actionable — for example, "Weight and pallet dimensions required for LTL rating — please resubmit with weight and L/W/H" is far more useful than "Missing info". The customer can resubmit with the same SKU once the original submission is rejected.
Reason-Code Guidance¶
There are no hard-coded reason codes — the field is free-form. Common patterns most operations teams use:
| Pattern | Example reason text |
|---|---|
| Missing data | "Dimensions required for pallet calculation — please add L/W/H and cases per pallet." |
| Duplicate | "Equivalent product already exists in the catalog as SKU WIDGET-001. Please use that SKU on your delivery request." |
| Non-compliant SKU | "SKU does not follow XXX-000 format required for this customer. Please resubmit as HDW-100." |
| Needs classification review | "Hazmat classification required before onboarding. Please email compliance@… with the SDS and resubmit." |
| Out of scope | "This product falls outside the categories in your current MSA. Contact your account manager." |
Once Reviewed, a Submission Is Locked¶
Both Approve and Reject require the submission to be in PENDING status. Attempting either on an already-reviewed submission returns a 400 error. If a mistake is made:
- For an erroneous rejection — the customer resubmits. You cannot "un-reject" from the UI.
- For an erroneous approval — deactivate the resulting catalog product through the normal Products admin flow. The submission record itself remains approved for audit integrity.
API Reference¶
All endpoints are prefixed with the platform API base URL and require a Bearer token.
| Method | Path | Permission | Purpose |
|---|---|---|---|
POST | /product-submissions | products:submit | Customer creates a submission |
GET | /product-submissions | products:view | List submissions (auto-scoped to customer for portal users) |
GET | /product-submissions/check-sku/:sku | products:submit | Live SKU-availability check |
GET | /product-submissions/:id | products:view | Submission details with reviewer and product relations |
PATCH | /product-submissions/:id/approve | products:manage | Approve and create catalog product |
PATCH | /product-submissions/:id/reject | products:manage | Reject with a required rejectionReason |
List filters supported on GET /product-submissions:
status[eq]—PENDING,APPROVED, orREJECTEDstatus[in]— comma-separated listcustomerId[eq]— staff-only scoping (portal users are always scoped to their own customer)search— matches name or SKUpage,pageSize,sortBy,sortOrder— standard pagination and sort (sortBy:createdAt,name,sku,status)
Response shapes follow the standard { data, meta } envelope used elsewhere in the API. Error responses use standard HTTP status codes: 400 for validation, 404 for not-found, 409 for SKU conflict.
Notifications¶
The event listener creates in-app notifications on three lifecycle events:
| Event | Recipients | Content |
|---|---|---|
product-submission.created | Staff with products:manage for the submission's warehouse | "New product submission — Name (SKU) from Customer" |
product-submission.approved | The submitting portal user (and their customer organization) | "Your product submission Name (SKU) was approved" |
product-submission.rejected | The submitting portal user (and their customer organization) | "Your product submission Name (SKU) was rejected — reason" |
Clicking a notification deep-links to the submission detail. In the staff UI this opens the Review modal; in the customer portal it opens the status view with the submission highlighted.
Audit Trail¶
Every state change is recorded through the platform audit module with entityType = product-submission:
| Action | When | Captured Fields |
|---|---|---|
CREATE | Submission is created | name, sku, status |
STATUS_CHANGE | Approval | status (PENDING → APPROVED), productId (null → new product ID) |
STATUS_CHANGE | Rejection | status (PENDING → REJECTED), rejectionReason |
The audit entries include the acting user ID and timestamp. For investigation and compliance queries, see the audit-log documentation.
Audit log doc — in progress
A dedicated audit-log reference is tracked under a separate ticket (issue #13). Once published it will live at administration/audit-log.md. TODO: convert to a proper link once #13 ships.
Troubleshooting¶
"User must be linked to a customer account to submit products"¶
Cause: The signed-in user has no customerId set on their profile.
Solution: A system administrator must edit the user in Users and set a default customer linkage, or the user must sign in with a portal account that belongs to a customer.
"No warehouse assigned. Please contact your warehouse representative."¶
Cause: The submitting user has neither a preferred default warehouse nor a warehouse data filter.
Solution: Set a default warehouse on the user's profile preferences, or add a warehouse data filter assignment.
"SKU already exists in the product catalog"¶
Cause: An active approved product already uses this SKU.
Solution: Use the existing catalog SKU on your delivery request — no submission is needed. If the existing product truly is different, pick a unique SKU.
"SKU is already pending approval"¶
Cause: Another submission (possibly your own, from earlier) is sitting in the PENDING queue with this SKU.
Solution: Check My Submissions — you may already have an open submission. Wait for that one to be reviewed, or pick a different SKU.
Approve button fails with "SKU was just taken"¶
Cause: Between opening the review modal and clicking Approve, another product grabbed the same SKU.
Solution: Expand Show Overrides, enter a different SKU in the SKU override field, and click Approve again.
"Cannot approve/reject a submission in APPROVED status"¶
Cause: The submission has already been reviewed. Approve and Reject only work on PENDING submissions.
Solution: See Once Reviewed, a Submission Is Locked.
Related Documentation¶
- Order Processing — how approved products flow into orders and delivery requests
- Customers — managing the customer accounts that portal submitters belong to
- Users — managing the
products:submit,products:view, andproducts:managepermissions - Bulk Upload — alternate path for importing large product catalogs (staff-only)
- Audit Log — in progress, tracked in issue #13
Support¶
For product-submission assistance:
- Email: support@zoratech.io
- FAQ: Frequently Asked Questions
- Troubleshooting: Common Issues