Agentic Workflow Architecture for HR
The most-probed technical area: how to design multi-agent systems that span HRIS, ATS, payroll, and ticketing, with the HITL gates and audit posture this domain demands.
Why this is the deep dive
Every loop will have at least one round that boils down to: "Design an end-to-end agentic workflow for [X]." Where X is onboarding, offboarding, a Q&A bot, survey synthesis, or a recruiting pipeline summary. The interviewer is testing whether you can:
- Decompose the workflow into the right granularity of agents and tools
- Identify where the human gates go
- Be explicit about the data flow and where PII crosses system boundaries
- Reason about failure modes before being asked
- Make the right n8n vs. Python vs. MCP allocation
This chapter gives you the framework. 06-applied-patterns applies it to specific workflows.
The shape of an HR agentic workflow
The reference architecture, top to bottom:
| Layer | Responsibility | Typical tool |
|---|---|---|
| Trigger | Webhook, schedule, manual kickoff | n8n trigger nodes, HRIS webhooks, Slack slash command |
| Context gather | Fetch the worker record, the ticket, the policy chunks | n8n HTTP nodes, MCP resources, RAG retriever |
| Agent reasoning | Plan, propose actions, draft output | Claude API via n8n's AI node or a Python service |
| Tool execution | Read systems (idempotent), draft side-effects (non-committing) | MCP servers, n8n function nodes, Python tool functions |
| HITL gate | Present proposed actions to a human; capture approval / reject | n8n Slack/email approval nodes, custom HITL UI |
| Commit | Execute the approved action (EIB upload, Slack send, Workday API call) | n8n + Python; always with idempotency keys |
| Audit log | Persist every step, prompt, tool call, decision, approver | Postgres or warehouse; durable, retained per policy |
| Observability | Metrics, traces, error rates | OpenTelemetry, your APM, structured logs |
If you can sketch this on a whiteboard from memory, you've cleared the bar. The rest is variant detail.
Multi-agent design for HR workflows
"Multi-agent" doesn't mean "many models gossiping in a chatroom." It means specialized agents with narrow responsibilities, orchestrated by a top-level supervisor.
Three composition patterns
- Supervisor + workers — a top-level agent decomposes the request and dispatches to specialized agents. Best for workflows with clear sub-tasks (e.g. onboarding: dispatch to
hire_drafter,provisioning_planner,welcome_scheduler). - Pipeline — agent A produces output that becomes agent B's input. Best for surveys (clusterer → sentiment scorer → narrative writer).
- Single agent + many tools — for simple workflows, one agent with a rich tool set beats decomposition. Don't over-engineer.
Start with single agent + many tools. Decompose only when (1) the system prompt is exceeding ~10K tokens and most of it isn't relevant per call, or (2) different sub-tasks need different models / different temperatures / different evals.
When multi-agent helps
- Different risk tiers in one flow — onboarding has low-risk (draft welcome email) and high-risk (commit Workday hire). Different agents → different model selection, different HITL policies.
- Cost optimization — Haiku for triage, Sonnet for drafting, Opus only when needed. Multi-agent makes the cost shape legible.
- Eval isolation — testing the clusterer separately from the writer is much cleaner than testing the end-to-end blob.
Why n8n is well-suited to HR specifically
This will come up. The reasons are not generic "n8n is nice." They are HR-specific:
- HRKX operators can read the flow. When you hand off to the People team, they can see what runs when, who approves, what the branches are. Code-first orchestration loses this.
- Built-in approval nodes. The HITL gate is the most-touched primitive in HR; n8n has Slack/email/webhook approval steps out of the box.
- Self-hostable. EU employee PII must often stay in EU infrastructure. Self-hosted n8n in your VPC is the simple answer.
- Native AI nodes. Claude, OpenAI, embedding, vector store — first-class. You don't fight the platform to drop in an agent.
- Connector breadth. Slack, Gmail, Google Workspace, Notion, Jira, HTTP — the actual day-2 tools HR teams already use.
- Error workflows. n8n's error workflow pattern lets you route failures to ops without per-node try/except.
What n8n is not good for, and what you'd say in interview:
"I put orchestration, approvals, retries, and connector glue in n8n. I put any heavy AI logic — tool definitions, eval harness, structured-output validation, complex prompt construction — in Python and call it from n8n. That keeps the visual flow readable for HRKX while the AI behavior stays testable and version-controlled."
Tool use across HR systems
The HR systems landscape is fragmented. A typical agent reaches into:
| System | Read patterns | Write patterns |
|---|---|---|
| Workday | RaaS reports, web service (Get_Workers), Prism queries | EIB upload (batch) or web service (transactional) — both HITL-gated |
| Ashby (ATS) | REST API: candidates, applications, jobs, offers | Stage moves, scheduling — usually low-risk, but offer-letter writes are HITL |
| Payroll (Workday / ADP / Deel / Remote) | Usually read-only from agents | Generally avoided. Payroll writes go through Payroll Ops |
| Ticketing (Jira / ServiceNow / Zendesk) | Tickets, comments, custom fields | Status changes, comments, assignments |
| Survey (Qualtrics) | Response export, free-text fields | Rarely write; sometimes survey-send |
| Equity (Carta / Shareworks) | Grants, vesting | Almost always no |
| IT provisioning (Okta / Jumpcloud) | Group membership, status | Often the agent requests; IT or Okta workflows execute |
Two design rules:
- Separate read and write tools sharply. Reads are safe, idempotent, cacheable. Writes are HITL-gated and idempotency-keyed.
- Writes that touch payroll are not auto-write tools — they are propose-tools. The tool's effect is to add a row to a "proposed actions" table, which then surfaces in a HITL UI.
MCP for sharing HRIS access patterns
MCP (Model Context Protocol) is how you stop re-implementing Workday access in every agent. The pattern:
- One canonical Workday MCP server — exposes read tools (
get_worker,list_supervisory_org,get_business_process_history) and resources (the schema, the field dictionary). - One canonical Workday-write MCP server — separate process, separate auth, separate audit. Exposes only "propose" tools that write to a staging table, never directly to Workday.
- Per-system MCP servers — Ashby, Qualtrics, the policy library, the ticketing system. Each agent picks which servers it connects to.
Benefits in interview-speak:
- One audit-trail discipline across all agents (every MCP call is logged identically)
- Access control at the MCP server, not per-agent (huge for compliance)
- HRKX can extend agents by composing existing MCP servers, no Python required
- Schema and resources are cached once — every agent inherits the prompt-cache savings
# Workday read MCP server — read-only, safe to expose broadly
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("workday-read")
@mcp.tool()
def get_worker(employee_id: str) -> dict:
"""Fetch a worker record by Workday employee_id."""
return workday_client.get_worker(employee_id)
@mcp.tool()
def list_supervisory_org(org_id: str) -> list[dict]:
"""List workers in a supervisory org."""
return workday_client.list_sup_org_members(org_id)
@mcp.resource("workday://schema/worker")
def worker_schema() -> str:
"""The Workday worker object's field dictionary, for prompt context."""
return WORKER_SCHEMA_DOC
if __name__ == "__main__":
mcp.run()
# Workday write MCP server — separate process, separate auth, HITL-gated
@mcp.tool()
def propose_job_change(employee_id: str, effective_date: str,
new_job_profile: str | None = None,
new_supervisor_id: str | None = None,
new_fte: float | None = None,
reason_code: str = "",
rationale: str = "") -> dict:
"""Propose a Workday job change. Does NOT commit. Writes to the
proposed_actions table and returns the proposal_id for HITL review."""
proposal = {
"kind": "job_change",
"employee_id": employee_id,
"effective_date": effective_date,
"fields": {k: v for k, v in {
"job_profile": new_job_profile,
"supervisor_id": new_supervisor_id,
"fte": new_fte,
}.items() if v is not None},
"reason_code": reason_code,
"rationale": rationale,
"idempotency_key": idem_key("job_change", employee_id, effective_date),
}
proposal_id = proposals_db.insert(proposal)
notify_hitl_channel(proposal_id)
return {"proposal_id": proposal_id, "status": "awaiting_approval"}
Read-MCP and write-MCP are different processes, with different credentials, in different VPC subnets. An agent that has only the read-MCP attached cannot accidentally propose a Workday change. This is the access-control story you want to be able to draw in interview.
HITL patterns in detail
The HITL gate is a UX problem as much as an engineering one. Four patterns, in increasing strictness:
| Pattern | How it works | Use for |
|---|---|---|
| Sampled review | Auto-commit; 5-10% sampled for human spot-check | Low-risk reads, non-PII summaries, ticket autoreplies on FAQ |
| Approve-or-edit | Slack/email card; human approves or edits + approves | Drafted policy answers, welcome emails, scheduled check-ins |
| Approve-with-diff | Human sees a structured diff of the proposed change before approving | Workday writes, EIB uploads, comp changes |
| Two-key approval | Two distinct approvers required (e.g. HRBP + Payroll Ops) | Severance, equity acceleration, anything cross-functional with money at stake |
The approval UI matters. A wall-of-text Slack message is useless. A structured card with "Worker: Jane Doe (W-12345). Current FTE: 1.0. Proposed FTE: 0.8. Effective: 2026-07-01. Reason: voluntary part-time. [Approve] [Reject] [Edit]" — that's the bar.
Worked example: leaver checklist agent
End-to-end, drawn from real interview answers that land well.
Trigger
HRBP marks a termination in Workday → Workday webhook → n8n trigger.
Context gather (read-MCP)
get_worker: identity, position, location, country, FTEget_compensation: salary, bonus eligibility, equityget_country_severance_rules: from policy library (MCP resource)get_open_tasks: Jira tickets, pending approvalsget_payroll_calendar: current and next two cycles
Agent reasoning (Sonnet, with tools)
System prompt frames: "you are drafting a leaver checklist; you propose actions, you do not commit; if the termination is within 5 business days of payroll cutoff, escalate."
Proposed actions (write-MCP)
propose_workday_termination: effective date, reason, last day workedpropose_okta_offboarding: groups to remove on last daypropose_final_pay_calculation: PTO payout, prorated bonuspropose_equity_treatment: per plan rulesdraft_offboarding_email(non-write tool — returns text)create_jira_ticket: knowledge transfer plan
HITL gate (Slack card)
HRBP approves the offboarding plan. Payroll Ops separately approves the final-pay calculation (two-key).
Commit
Approved proposals execute via n8n. Idempotency keys ensure retry safety. Audit log captures every step.
What an interviewer is listening for
- You named the payroll-cutoff escalation rule unprompted
- You separated read and write MCP servers
- You proposed two-key approval for payroll-affecting steps
- You called out the audit log and idempotency without being asked
Anti-patterns to call out (and avoid)
- "The agent will write directly to Workday." Never propose this. Writes go through a proposed-actions queue with HITL.
- "The agent has access to the payroll system." No. Payroll reads are usually fine; payroll writes are off-limits.
- "Multi-agent because it's cool." If a single agent with 6 tools does the job, that's the right answer.
- "We log decisions but not prompts." Log everything: input, retrieved context, prompt, tool calls, tool results, output, approver, timestamp. Audit is a regulatory requirement, not a feature.
- "We retry payroll-touching writes automatically." Never. Idempotency keys help, but the retry policy is "fail closed, page a human."
Framings worth memorizing
- "I split read-MCP and write-MCP into separate processes with separate credentials. That gives me access control at the protocol boundary, not per-agent."
- "For payroll-adjacent writes, my tool doesn't commit — it proposes. The orchestrator queues the proposal, the HITL UI presents the diff, the human approves, the commit happens through a separate worker that owns idempotency."
- "n8n owns orchestration, branching, scheduling, approvals. Python owns heavy AI logic and the MCP servers. They meet at well-defined HTTP boundaries."
- "My default architecture is single-agent + many tools. I decompose to multi-agent only when sub-tasks need different models or different evals."