A quotation approved in chat has no state. It has a thread. A week later nobody can say whether it was approved, replaced, or answered with “ok.”
The states are the domain. Model that before the controllers. Scoping a custom system is where you name this boundary, before a feature list grows a status dropdown with every word someone might type. Internal tools and customer-facing apps are scoped differently. An approval flow is an internal tool. It should not be designed like a marketing page.
The states we mean
| State | What is true |
|---|---|
| Draft | The author can still change it |
| Submitted | A named approver has it. The author does not silently edit the amount |
| Approved | That number stands. A change is a new revision, not an overwrite |
| Rejected | Kept, with a reason. Not deleted |
| Expired | The date passed. Payment does not make it approved |
Allowed moves are few. Draft goes to submitted. Submitted goes to approved or rejected. Approved does not return to draft. A rejected document can be copied into a new draft. It is not edited until it looks approved.
Record who moved it and when. That row is the audit. Email is a notification, and the notification belongs on a queue, not in the transaction that wrote the state.
What the screen must not do
- Offer every state to every role. The receptionist does not get “approved.”
- Treat delete as a synonym for reject.
- Leave two documents marked current for one request.
If the approver is “whoever sees the chat,” you do not have a workflow yet. Name the role, then build the transition.
Straight next step
Send one document that is approved in chat today — a quote, a discount, a purchase. We will name the states and who may move them. The screens come after that, on a custom system if the thread is actually how the business decides.



