The Daytime Desk on Microsoft 365
Three flows and four lists for the office admin's day that is not about money: the phone in business hours, and the running-late message that stops the next call.
Used by
- Office Administrator
- Business Systems Analyst
- Scheduling Coordinator
How to get this live in your business
The temptation on this stack is to copy each job’s arrival time into a list so the desk can read it quickly. Then the board moves the time, the list does not, and the desk tells a customer a time that was true this morning. Nothing here copies the job. The lists hold what the business sets, the roster, and what the desk did: the calls it took, the callbacks it raised, the messages it sent.
In short
What this is
A build plan for the client desk standard on Microsoft 365. Four lists, three flows, and the arrival time read from the job system every time it is needed.
The core rule
A status answer and a running-late message both read the job by its id at the moment they need the time. No list holds one.
What it does not do
It does not hold a job, an arrival time or a contact, and a second delivery of the same schedule change cannot make a second message.
The hard part
The phone line itself is not a Power Automate job: it is the step not to hand-roll. On this stack the hard column is Updates.ChangeKey, which makes a repeated change fail at the list.
Take the spec to Claude, ChatGPT or Copilot. The sentence to paste is one click away.
Want the whole thing by email?
This paper, plus the rest of the library. One click to stop them.
1. Before you start
Read the client desk build standard first. It defines its objects, and the lists below are those objects with SharePoint types on them. Shared conventions are in building the standards on Microsoft 365.
Decide where the daytime calls are answered. The voice on the line, with its disclosure and its transfer, is not built in Microsoft 365. Whatever answers calls the flows below over HTTP: look the caller up, read the job, raise a callback.
Then fill the Roster. One row per shift, with the person on duty and how soon a callback is due. Every transfer and every callback reads it, so a blank shift is a callback with nobody on it.
One thing to check before you build: does your job system send an event when an arrival time changes, or a job is marked on the way or complete? If it does, Flow 3 triggers on it. If not, Flow 3 polls on a schedule and keeps a watermark. A message sent because someone remembered to click a button is not the standard.
2. The lists, with their columns
For your developer. Show the fields for your developer
Site: ClientDesk
LIST Roster (one row per shift. A person keeps this current.)
ShiftStart DateTime indexed, REQUIRED
ShiftEnd DateTime REQUIRED
OnDuty Person REQUIRED. takes a transfer and owns a callback during the shift
CallbackWithinMinutes Number REQUIRED
LIST Calls (one per daytime call the desk answered)
CallId Text indexed, UNIQUE
JobId Text indexed. the open job the caller was matched to, or the job written. Blank when the write failed.
Wanted Choice REQUIRED. new_work | status | person | other
Outcome Choice REQUIRED. answered_from_job | transferred
| callback_raised | job_written | write_failed
DisclosedAt DateTime REQUIRED. before the first question, on every call
NotedOnJobAt DateTime when the call was written back to the job
LIST Callbacks (one per callback. Never a chat reminder.)
CallId Text indexed
JobId Text indexed. or blank, with the caller's number, when no job was written
CallerPhone Text
Reason Choice REQUIRED. status_unknown | missed_transfer
| write_failed
Owner Person REQUIRED. from Roster, never typed
DueBy DateTime REQUIRED
Status Choice indexed. open | done
LIST Updates (one per message sent, or held for want of consent)
ChangeKey Text indexed, UNIQUE. JobId|Kind|Eta. One change, one row, one message.
EventId Text indexed. the schedule change that woke it
JobId Text indexed, REQUIRED
Kind Choice REQUIRED. late | on_the_way | complete
Eta Text read from the job after the change, never typed
SentTo Text the contact on the job
Status Choice REQUIRED. sent | held_no_consent
Nothing in this site holds a job's arrival time or its contact. The flows read both from the job system at the moment they need them.Updates.ChangeKey is unique: the job id, the kind and the time, joined. The same change twice fails to save; a new time is a new key and a new message.
3. Every rule, and what enforces it
| The rule | Enforced by | How |
|---|---|---|
| Disclosure first, on every daytime call | Calls.DisclosedAt | Required. The line stamps it before the first question; a Calls row cannot be saved without it. |
| The caller is looked up before anything is written | Calls.JobId | Flow 1 reads the job system for an open job on the number first and writes the matched job id. A new job is written only when that read finds none. |
| A status question is answered from the job, or not answered | Flow 1 and Callbacks.Reason | Flow 1 reads the job by id and returns its arrival time as it stands. When it holds none, Flow 1 returns no time and raises a Callbacks row with reason status_unknown. |
| A missed transfer becomes a callback with a name and a time | Callbacks.Owner and Callbacks.DueBy | Owner is a required Person copied from the current Roster row, and DueBy is now plus CallbackWithinMinutes. Neither is typed. |
| Messages only when the record changes, once per change | Updates.EventId and Updates.ChangeKey | Flow 3 is triggered by the schedule change, reads the job, and writes the Updates row before it sends. A duplicate ChangeKey stops the flow. |
| Only to the contact on the job, only with consent | Updates.SentTo and Updates.Status | Flow 3 reads the contact and the consent flag from the job. No consent writes held_no_consent and a note on the job, and sends nothing. |
| Every call and every update is written back to the job | Calls.NotedOnJobAt | Each flow writes a note to the job in the job system, by its id, and stamps the time. Power BI counts Calls with no NotedOnJobAt. |
4. The three flows
For your developer. Show the fields for your developer
FLOW 1 Caller lookup (called by the line, HTTP)
input call id, caller's number, what they asked
logic read the job system for an OPEN job on that number
found -> return the job id; note the call on the job
asked when -> read the job by id, return its eta
no eta -> return none; raise Callbacks
(status_unknown, owner from Roster)
none -> write the job (verbatim, fixed type), read it
back by id; blank id -> Callbacks (write_failed)
never return a time the job does not hold
FLOW 2 Missed transfer (called by the line, HTTP)
input call id, job id
logic read the current Roster row
write Callbacks: Owner = OnDuty,
DueBy = now + CallbackWithinMinutes
note the callback on the job
FLOW 3 Status message (schedule change, by event or poll)
trigger eta moved later, on the way, or complete
logic read the job by id: eta, contact, consent
write Updates with ChangeKey = JobId|Kind|Eta
duplicate -> stop
no consent -> Status held_no_consent, note, stop
send to the job's contact; note it on the jobNone of the flows holds a time between runs. Each one reads the job when it needs the time, which is the whole defence against the stale answer.
5. The order to build it in
- Create the four lists and fill the Roster. Make ChangeKey unique.
- Build Flow 3 first. The running-late message stops more calls than anything else here, and it needs no phone line.
- Move one job's arrival time twice and confirm two Updates rows and two messages; deliver the same change twice and confirm one.
- Build Flows 1 and 2, then connect the line that answers your calls to them.
- Ring from a customer's phone, ask when, ask for a person, and let the transfer go. Check the Calls row, the Callbacks row and the notes on the job.
6. Three traps specific to this build
Copying the arrival time into a list
It is faster to read and it is wrong by lunchtime. The board moves the job; the list does not. Read the job by id every time.
Triggering the message on every edit to the job
A job is edited for many reasons. Trigger on the arrival time, the on-the-way flag and the status, and let ChangeKey refuse the repeats.
A callback assigned to a text field
Somebody types a first name and it reaches nobody. Owner is a Person, copied from the Roster row for the shift.
7. Where the automation stops
- Every transfer, once it is answered.
- Every callback, by the person the roster names.
- Moving the arrival time. The flows read it; they never write it.
- Keeping the Roster current.
The pass test. On your own number and job system, in business hours. Ring from a customer’s phone, ask when the tradesperson is coming, then ask for a person and let the transfer go unanswered. Then move that job’s arrival time. The time said on the call must be the time the job held. One Callbacks row must exist, owned by the person on the Roster. Exactly one Updates row and one message must exist for the change, with the new time, and the job must carry notes for the call, the callback and the message.