Key takeaways
A work request is somebody telling maintenance that something needs attention. Nothing has been promised, nobody has been assigned and no budget has moved.
A work order is the plant committing labour, parts and time to a named asset. It has an owner, a priority, a planned duration and a cost.
One request can become no work order at all, one work order, or a task added to a job that was already scheduled. Two requests can also converge on a single work order.
| Case | What exists |
|---|---|
| Guard rattle reported, screener finds it within spec | Request, no work order |
| Two shifts report the same leak | Two requests, one work order |
| Temporary fix now, full repair at the shutdown | One request, two work orders |
| Ask already covered by a planned job | Request, an existing work order |
Collapse request and work order into one table and the rows that never became work have no key of their own. They get edited into work orders, or they get deleted.
On the month worked through below, 60 requests produced 31 conversions. A single-table system therefore shows 31 rows where 60 people asked for something.
That hides 29 requests, 48.3% of everything raised. The hidden half is the interesting half: the rejections, the duplicates and the asks that were never answered at all.
The reporting consequence is precise, and it is worth stating slowly. Your backlog report becomes a report about your own decisions rather than about the plant, because the only demand it can see is demand you already agreed to.
The conceptual difference, argued with examples, belongs to our guide on work order vs work request. How the intake process runs day to day is covered in maintenance work request system.
What this page adds is the record itself: the fields, the nine statuses and their allowed transitions, rejection and duplicate handling, the response time definitions, and the join to the work orders that answer a request.
It does not re-specify the work order. That belongs to our production work order data model and the wider CMMS data model.
Twelve fields carry the ask. Everything else on a request is a decision about the ask, and decisions belong in the screening record further down.
| Field | Type | Why |
|---|---|---|
| request_ | id | Mandatory. Own key, quotable back to the requester |
| asset_ | id | Mandatory. The machine or location the ask is about |
| line_ | id | Optional. Fills from the asset, kept for reporting |
| request_ | enum | Mandatory. Repair, inspection, improvement, safety |
| title | text | Mandatory. One line, capped at about 80 characters |
| description | text | Mandatory. What was seen, heard or measured |
| requested_ | enum | Mandatory. U1 to U4, the requester's own ask |
| production_ | enum | Mandatory. Stopped, slowed, quality risk, none yet |
| submitted_ | timestamp | Mandatory. Server time, never typed by a person |
| source_ | enum | Mandatory. QR scan, mobile app, web, phone relay |
| request_ | enum | Mandatory. One of the nine states below |
| attachment_ | int | Optional. A photo is the cheapest evidence you own |
asset_id must point at a row, never at typed text. A request against "the filler" is unusable the moment you own three fillers, and no later clean-up recovers which one was meant.
Where the asset register stops and a location starts is its own design question, covered in our asset hierarchy data model. The short rule for requests: point at the deepest node the requester can actually identify from where they stand.
production_impact is separate from urgency on purpose. Impact is an observation about the line right now, and urgency is an opinion about how soon somebody should come.
Six fields identify who asked and how to answer them. All of them are cheap, and the first one is the one plants most often get wrong.
| Field | Type | Why |
|---|---|---|
| requested_ | id | Mandatory. Employee record, never a typed name |
| requester_ | enum | Mandatory. Operator, supervisor, quality, safety |
| requester_ | id | Mandatory. The shift worked, not the wall clock |
| on_ | id | Optional. Who actually saw it, for a relayed call |
| requester_ | text | Optional. Only where contractors raise requests |
| notify_ | boolean | Mandatory. Defaults to yes, drives the push message |
requester_shift matters because it is the only honest way to compare intake across a plant. Without it a raw daily count cannot tell you whether a quiet shift was quiet or simply did not report.
notify_on_decision is not a nicety. A requester who never hears an answer stops raising requests, and intake falls without a single person deciding that it should.
Screening is a separate record because it is a separate act by a separate person. Ten fields carry it, and every one of them answers a question an auditor will ask.
| Field | Type | Why |
|---|---|---|
| screened_ | id | Mandatory. A person, never a queue or a role |
| screened_ | timestamp | Mandatory. Stops the request-to-screen clock |
| decision | enum | Mandatory. Approve, reject, duplicate, merge, close |
| decision_ | enum | Mandatory. Coded from a fixed list, not free text |
| decision_ | text | Mandatory on reject and on merge |
| criticality_ | enum | Mandatory. Copied, so a re-rating cannot rewrite history |
| work_ | enum | Mandatory on approve. P1 to P4, the scheduler's call |
| duplicate_ | id | Mandatory when the decision is duplicate |
| merged_ | id | Mandatory when the decision is merge |
| target_ | timestamp | Optional. Derived from priority, an internal promise |
criticality_at_screen is the field nobody specifies and everybody later wishes they had. Asset criticality gets re-rated once or twice a year, and a live join silently rewrites every historical decision the next morning.
decision_reason has to be coded. A free text reason cannot be counted, and the whole point of screening data is to count how often each reason fires.
The join is many to many, so it needs its own table. A foreign key on either side will be wrong within a month of going live.
| Field | Type | Why |
|---|---|---|
| link_ | id | Mandatory. Own key, because the join is many to many |
| request_ | id | Mandatory. Which ask |
| wo_ | id | Mandatory. Which commitment |
| link_ | enum | Mandatory. Answers fully, answers partly, follow-up |
| linked_ | id | Mandatory. The person who raised or attached the job |
| linked_ | timestamp | Mandatory. Stops the request-to-work clock |
| coverage_ | text | Optional. Why this job answers this ask |
linked_by is the field that keeps the record honest about who acted. A person screens, a person approves and a person raises the work order, and the link row is where that person is named.
On the month below, 31 converted requests produced 27 distinct work orders, because four requests were attached to a job that already existed. That is 31 ÷ 27 = 1.15 requests per work order.
The counting trap sits right there. "Work orders from requests" is 27 and "requests answered by work" is 31, and the two are different questions that look like the same one.
Nine states, and every one of them is a real outcome somebody has to be able to report on. A two-state model, open and closed, throws away everything worth knowing.
| From | Allowed next |
|---|---|
| Submitted | Screened, Duplicate, Withdrawn |
| Screened | Approved, Rejected, Merged, Closed without work, Withdrawn |
| Approved | Converted, Withdrawn |
| Converted | Terminal |
| Rejected | Terminal |
| Duplicate | Terminal |
| Merged | Terminal |
| Withdrawn | Terminal |
| Closed without work | Terminal |
Six of the nine are terminal, and that is deliberate. A request that has been answered should not be reopened, because reopening it destroys the response time you already recorded against it.
| State | Moved by | Must be filled |
|---|---|---|
| Submitted | Requester | Header fields, submitted_ |
| Screened | Screener | screened_ |
| Approved | Approver | work_ |
| Rejected | Approver | rejection_ |
| Duplicate | Screener | duplicate_ |
| Merged | Screener | merged_ |
| Converted | Planner | A link row with wo_ |
| Withdrawn | Requester | withdraw_ |
| Closed without work | Screener | closure_ |
Two rules make this table enforceable rather than decorative. A state cannot be entered until its mandatory fields are present, and a person is named on every transition.
Notice also that Converted is the planner's move and requires a link row. A request never changes itself, and no system should raise work off the back of one without a person confirming it.
A rejected request is one of the most valuable rows in the whole model. It is a record of demand that the plant consciously chose not to meet.
Deleting it, or letting the requester's screen simply stop showing it, destroys that evidence. Six fields make a rejection auditable.
| Field | Type | Why |
|---|---|---|
| rejection_ | enum | Mandatory. From the short fixed list below |
| rejection_ | text | Mandatory. One line the requester will actually read |
| rejected_ | id | Mandatory. A person, never a department |
| rejected_ | timestamp | Mandatory. Server time, stops the decision clock |
| notified_ | timestamp | Mandatory. Proof the answer reached the requester |
| appeal_ | id | Optional. Points back when the ask is re-raised |
notified_at is the field that changes behaviour on the floor. It is the difference between a decision and a decision somebody heard about.
Six rejection codes are enough for almost any plant. More than about eight and screeners start picking the first plausible one in the list.
| Code | Meaning |
|---|---|
| RJ1 | Not a fault, equipment working as designed |
| RJ2 | Already covered by a planned job |
| RJ3 | Outside maintenance scope, routed elsewhere |
| RJ4 | Asset retired or out of service |
| RJ5 | Not enough information, requester did not respond |
| RJ6 | Risk accepted and recorded for review |
RJ6 is the one that must exist and the one vendors rarely offer. Some requests are turned down knowingly, with the risk accepted by a named person, and that is a completely different fact from "not a fault".
Watch RJ2 as well. Requests rejected because a planned job already covers them tell you the preventive maintenance schedule is not visible to the people on the machines.
A duplicate_of pointer says this row describes the same problem as that row. It does not say this row should never have existed.
The pointer creates two legitimate counts: raw requests, and distinct problems.
On the month below, 60 requests were raised. Nine were duplicates and three were merged into other requests, so distinct demand is 48.
The raw count is therefore 25.0% higher than the distinct count, since 60 ÷ 48 = 1.25. Neither number is wrong and quoting either one without its definition is.
Marking a duplicate keeps the row and adds a pointer. Merging, as most systems implement it, moves the content of one request into another and leaves one row where there were two.
That silently destroys the demand signal. The second requester vanishes, the second submitted_at vanishes, and the response time for that person is never measured because their request no longer exists.
If you merge, record the merge. Keep the source row in state Merged with merged_into populated, its own submitted_at, its own requester and its own screened_at.
Requests generate their own clocks, and those clocks measure the maintenance department's responsiveness rather than the length of a repair. Three intervals matter, and they are not interchangeable.
| Interval | Formula |
|---|---|
| Request to screen | screened_ |
| Request to approval | approved_ |
| Request to work start | wo actual_ |
| Request to answer | notified_ |
Each interval needs its denominator printed beside it, because the four run over different row sets.
| Interval | Denominator |
|---|---|
| Request to screen | Requests with a screened_ |
| Request to approval | Requests that reached Approved |
| Request to work start | Link rows whose work order has actually started |
| Request to answer | Requests with a notified_ |
Two of these a plant can measure honestly from request fields alone. Request to screen and request to approval use only submitted_at, screened_at and approved_at, all of which live on the request.
Request to answer is measurable too, and it is the one requesters care about, but only if notified_at is written when the message is actually sent.
Request to work start is different. It reaches into the work order table for an actual start time, and it is only as good as the discipline around starting jobs in the system rather than at the machine.
None of these is repair time. Repair time starts when a technician picks up the tools and ends when the asset works again, which our guide to mean time to repair defines properly.
The confusion is expensive because the two move independently. A plant can cut request to screen from two days to two hours and not change MTTR by a minute, and that is a real and reportable improvement.
Almost every system ships with one field called priority, and almost every plant fills it with three different meanings. The result is a column where nobody can tell who made the call.
| Field | Who sets it |
|---|---|
| requested_ | The requester, at the machine |
| criticality_ | The asset register, copied at screening |
| work_ | The screener or planner, as the final call |
Urgency is an opinion, and it is allowed to be one. The operator is telling you how much this is hurting them right now, and that is information you want even when it is wrong.
Criticality is a property of the asset, reviewed annually. It knows nothing about this morning.
Priority is the decision, made by someone who can see both the urgency and the criticality plus the schedule, the parts and everybody else's requests.
Three requests from the same week on line LINE‑03, with urgency, criticality and the final priority side by side.
| Request | Urgency, criticality | Priority |
|---|---|---|
| REQ‑2026‑0412 guard interlock on the labeller | U1 and C3 | P1 |
| REQ‑2026‑0418 oil weep on the extruder gearbox | U4 and C1 | P2 |
| REQ‑2026‑0423 noisy bearing, standby palletiser | U1 and C4 | P4 |
REQ‑2026‑0412 is a safety interlock on a machine rated C3. Criticality does not govern here, and the screener raised it to P1 despite the asset being unremarkable.
REQ‑2026‑0418 came in as a low urgency note from a planner walking the line. The asset is C1, a weep becomes a leak, and the screener raised it to P2 against the requester's own opinion.
REQ‑2026‑0423 is the uncomfortable one. The operator wanted it now, the palletiser is a standby unit, and the screener lowered it to P4 and had to write a reason the operator would read.
Now collapse the three fields into one. REQ‑2026‑0423 arrives as P1 and stays P1, or the screener overwrites it to P4 and the operator's U1 is gone forever.
Across the 44 decided requests in the month below, urgency and final priority matched on 24. The screener lowered 14 and raised 6, so 20 of 44, or 45.5%, were changed.
Line LINE‑03, one calendar month, 60 requests raised by operators and supervisors. Every figure below comes from these rows.
| Outcome | Requests |
|---|---|
| Converted to a work order | 31 |
| Approved, no work order yet | 5 |
| Rejected | 6 |
| Duplicate of another request | 9 |
| Merged into another request | 3 |
| Withdrawn by the requester | 2 |
| Closed without work | 2 |
| Still awaiting screening | 2 |
| Total raised | 60 |
Check the arithmetic before anything else: 31 + 5 + 6 + 9 + 3 + 2 + 2 + 2 = 60. A month that does not reconcile to the raw count has rows in a state nobody has agreed on.
Requests that reached Approved are 31 + 5 = 36. Requests that were screened at all are 60 minus the 2 still waiting, so 58.
The 31 conversions produced 27 work orders, four of them attached to jobs already on the schedule. Distinct demand, after removing 9 duplicates and 3 merges, is 48.
Both methods use the same 60 rows and the same definition of "approved". Only the denominator moves.
| Method | Result |
|---|---|
| A. 36 ÷ 60 raised | 60.0% |
| B. 36 ÷ 44 decided | 81.8% |
Method B divides by the requests that received a genuine screening decision on distinct demand: 31 + 5 + 6 + 2 = 44. It excludes the 9 duplicates, the 3 merges and the 2 withdrawals, none of which is a decision about a distinct problem, and the 2 still waiting.
Method A gives 36 ÷ 60 = 60.0%. Method B gives 36 ÷ 44 = 81.8%.
The gap is 21.8 percentage points, from a denominator that moved by 16 requests. Nothing about the plant changed between the two calculations.
Both are defensible, and a vendor quoting an approval rate is quoting one of them without telling you which. Ask, and write the answer into your reporting standard on the day you go live.
Every screened request carries screened_at minus submitted_at in elapsed hours. Here is the whole distribution, once for all 58 screened requests and once for the 44 distinct decided ones.
| Hours | All 58 | Distinct 44 |
|---|---|---|
| 1 | 9 | 1 |
| 2 | 7 | 4 |
| 3 | 6 | 4 |
| 4 | 5 | 4 |
| 6 | 5 | 5 |
| 8 | 4 | 4 |
| 12 | 4 | 4 |
| 20 | 5 | 5 |
| 30 | 5 | 5 |
| 48 | 4 | 4 |
| 72 | 4 | 4 |
| Total | 58 | 44 |
With 58 values the median is the average of the 29th and 30th. The cumulative counts run 9, 16, 22, 27, 32, so both of those values sit in the 6 hour bin and the median is 6.0 hours.
With 44 values the median is the average of the 22nd and 23rd. The cumulative counts run 1, 5, 9, 13, 18, 22, 26, which puts the 22nd at 8 hours and the 23rd at 12 hours.
That gives (8 + 12) ÷ 2 = 10.0 hours. Same field, same month, same clock: 6.0 hours or 10.0 hours.
The gap is 4.0 hours, a median 66.7% higher on a row set 14 requests smaller. Again, nothing on the line changed.
The 14 excluded rows were not a random sample. Duplicates and withdrawals are screened fastest, because recognising an ask you have already seen takes seconds.
Eight of the nine fastest requests in the month were duplicates or withdrawals. Remove them and the fast end of the distribution collapses, so the median walks up the tail.
This is the whole lesson of the example. A request metric is a statement about a row set, and the row set is a choice somebody made, usually without writing it down.
The defence is a single line in the report header naming the population. "Median request to screen, 58 screened requests, duplicates and withdrawals included" is reproducible and "median response time 6 hours" is not.
The same discipline applies to backlog age, which is another median over a row set that changes shape as you clear it. Our guide to maintenance backlog management works through that side of it.
Question five is the one that separates the tools. Any system will show you an average response time, and very few will print the population that produced it, which is the difference worth testing when you compare equipment maintenance software.
Fabrico is an OEE platform with a full CMMS built in, so requests, work orders and machine data sit in one system.
Requests move through a status workflow with approval workflows, and there is a dedicated emergency and reactive flow that records reaction time for the urgent ones.
Your team raises them from the iOS, Android or web app, with QR scanners on machines and parts so the right asset is attached without typing, and fast or detailed task creation depending on who is filling it in.
Push, in-app and email notifications with smart rules carry the decision back to the person who asked, and the audit log keeps who did what.
The analytics cover task distribution, per-employee productivity, MTTR and MTBF, with Excel export, a REST API and SAP and other ERP connectors when the numbers have to leave the system.
What Fabrico does not do: it does not plan or schedule production, it does not order parts on its own, and it has no predictive maintenance product.
A work order is always created and confirmed by your team. A person screens the request, a person approves it and a person raises the job.
Everything else on this page, including the nine states, the rejection codes and the link table, is a specification to test a vendor against rather than a feature list.
Want to see how your requests would flow from the machine to a decision? Book a 30 minute demo with a Fabrico consultant, no commitment, or contact us with your questions.
A work request records that somebody asked for attention, with no labour, parts or time committed. A work order is the plant agreeing to do the work, with an owner, a priority and a cost.
The header needs request_id, asset_id, request_type, title, description, requested_urgency, production_impact, submitted_at, source_channel and request_status. The requester needs requested_by, requester_role and requester_shift, and screening adds screened_by, screened_at, decision and decision_reason.
No, it is evidence of demand the plant chose not to meet. Keep it with a rejection code, a note, the name of the approver and a timestamp proving the requester was told.
Nine: submitted, screened, approved, rejected, duplicate, merged, converted, withdrawn and closed without work. Six of them are terminal, and each one has a named person who may move it and a set of fields that must be filled first.
No. Response time measures how fast the department reacted to an ask, while MTTR measures how long the repair itself took once it started.