A recruitment workflow for a UAE engineering company of about 2,000 employees, built on the Microsoft 365 tools its staff already used. It went from the first meeting with HR to rollout in three business units in 101 days. An AI step turns each CV into a candidate record, and people make every hiring decision.
The executive vice president wanted to see where hiring stood and why it was slow. The answer was spread across inboxes and recruiters’ own files, so I started by mapping the process with the HR manager.
A request began as an email to a vice president, and each business unit ran its approvals its own way. Once that email was sent, a manager could expect HR to start sourcing while approvals were still open, so nobody could say when the hiring clock had started.
After approval, each recruiter tracked candidates, dates and remarks in a personal spreadsheet. CVs travelled as email attachments, and interview feedback forms were emailed out and chased by hand. When a hire ran late, the delay was argued in meetings from email chains, because no record showed which stage had taken the time.
Management saw a snapshot that HR assembled before each monthly review.
Each hand-off got a rule that the workflow enforces, and the decisions about candidates stayed with people.
| Hand-off | Before | With the system |
|---|---|---|
| Request → approval | An email to a vice president, approved in each unit’s own way | A form creates the request with its own ID and sends it to the unit’s vice president; senior grades go on to the executive vice president |
| Approval → HR | HR could be asked to start sourcing before approval | HR gets access only after the required approvals. The request is then assigned to the unit’s recruiter, with a folder for CVs and a sourcing deadline |
| CV → record | A recruiter read each CV and typed the record, about 10 minutes each | The recruiter drops the CV in the request’s folder and an AI step creates the record, about 1 to 2 minutes of the recruiter’s time Estimate |
| Candidate → manager | CVs went to managers by email, and answers came back the same way | Each manager gets one card in Teams listing everyone waiting for review, and shortlists or rejects there, with a reason for a rejection |
| Interview → feedback | Feedback forms were emailed out and chased by hand | After an interview, each panel member gets a feedback link tied to that interview, and HR is told when all of it is in |
| Key stage → deadline | Delays surfaced at the monthly review | Each key stage has a deadline counted in weekdays. The workflow warns before it, and a missed deadline is escalated with the vice president copied |
A new hiring request needs your approval. Open the request to see it in full.
MPR-OPS-031 · Site supervisor
Interview feedback for MPR-OPS-031-C04 is past its deadline.
I’d start by following one hiring request from the manager’s first ask to the new hire’s first day.
That shows who has to approve it, when HR takes ownership, what each reviewer must do and where the delays hide. Those answers set the workflow and its deadlines. The first version runs on the tools your team already has, and AI takes on the data entry your recruiters should not be doing by hand.
Discuss a system like thisThe sponsor set the goal, visibility into hiring, and first pictured a shared tracker. After mapping the process with HR, I proposed building it into Microsoft 365, so approvals and notices would run on the accounts and Teams chats staff already used, with Python on Azure for the CV work.
In version 2.0, eleven Power Automate flows run the process over six SharePoint lists: requests, candidates, interviews, feedback, an AI log and a configuration list. Each flow does one job, and most hand over by changing a record’s status, so the process behaves like a state machine. Four kinds of event move it: a status change, a new file in a CV folder, a person’s action (a form, a card answer or a recruiter’s command in a Next action field), and a schedule for deadlines and review cards.
Confidentiality is set on each request and candidate record. When one is created, the flow stops it inheriting the site’s permissions and grants access to the people on it: the requester, the hiring manager and the approver, then the recruiter once the request is approved. A filtered view only hides rows on screen; these permissions decide who can open the record at all.
Deadlines count working days, weekends excluded, from the moment a stage starts. Deadline lengths, the grade that needs the executive vice president and each unit’s approver sit in the configuration list, so an administrator can change them without editing a flow.
Power Automate handles what Microsoft 365 does well: approvals, SharePoint events, Teams cards, email and calendar invites. Reading CVs needs real parsing and model calls, so that work sits in one Python function on Azure that the ingestion flow calls with the file.
A recruiter drops a CV into the request’s folder. A Python function on Azure then makes three separate model calls, and code replaces the candidate’s personal details before the call that assesses them.
This three-call version was committed the week after rollout, on 24 September 2025, with its tests the next day. The ingestion flow wraps the call in try, catch and finally. A file the function rejects or cannot read is moved to a failed folder and logged, so a bad file cannot trigger the flow again and again; a CV that goes through moves on to the hiring manager’s review. The section below follows one fictional CV.
The recruiter saves the CV in the request’s New CVs folder. The flow checks for new files every minute, reads which request the file belongs to from its folder, and sends the file to the function with the job title.
PDFs are converted page by page, and a page whose opening text is missing from the conversion gets its raw text added back. Since December 2025, a PDF averaging under 50 characters a page is treated as a scan and read with OCR. Word files are read directly; other file types, and files over the size limit, are refused.
The first call returns the candidate’s profile as JSON that has to match a strict schema: name, contact details, current title, experience and qualifications. Pattern matching fills in any email address, phone number or profile link the model misses.
The second call only lists the personal details it finds, such as names, employers, schools, addresses and ID numbers, with every spelling of each. Code then replaces them, longest first, with placeholders such as [CANDIDATE_NAME] and [COMPANY_1]. The model points at the text; code does the replacing, the same way every time.
The third call reads only the redacted text and returns skills, experience and a fit score against the job. Each call writes a row to the AI log with its stage, model, prompt, tokens, time and outcome, and the candidate record is created under the request with its own ID. The score is advice. The hiring manager shortlists or rejects.
A review card stays in the manager’s chat, so it can be answered twice. The flow that receives the answer checks whether a decision is already recorded before it writes one, then confirms back to the manager.
People work with IDs such as MPR-OPS-031 and MPR-OPS-031-C04, which show the unit, the request and the candidate at a glance. Every flow updates records by SharePoint’s internal item ID.
The workflow lost one manual step on the way to version 2.0, and the CV pipeline learned from the CVs people actually send.
Real CVs arrive as scans and two-column layouts, and each change below taught the pipeline to read them. Each row traces to a commit or to the project’s own documents.
The workflow runs in three business units and records each request’s stages and deadlines as the hiring happens.
Every request carries live counts from its candidates, and integrity checks flag records that need a second look, such as a candidate marked as joined with no joining date. A version for your company starts from the same method, an owner and a deadline at every step, and adds what your hiring needs.
If your managers ask HR for every update and your reviews start by rebuilding the status, the fix starts with one request traced from approval to joining. Bring one.