AI agents can automate many tasks, but not every decision should happen without human involvement.
A long-running agent may analyze documents, update records, approve requests, send notifications, or interact with external systems. Some of these actions can have real business consequences. In such cases, allowing the agent to make the final decision automatically can create unnecessary risk.
A better approach is to pause the workflow at important decision points and ask a human to review the proposed action.
This is commonly called human-in-the-loop.
In this article, we will see how to design human approval into a long-running agent workflow in C#, how approval state should be handled, and what developers should consider when building this pattern for production applications.
What Is Human Approval in an AI Agent?
Human approval means that the agent can perform analysis and prepare an action, but a person must approve that action before the workflow continues.
A simple workflow might look like this:
User Request
|
v
AI Agent
|
v
Analyze Information
|
v
Prepare Action
|
v
Human Approval
|
+---- Reject ----> End
|
+---- Approve ---> Continue
|
v
Execute ActionThe important point is that the human is not necessarily involved in every step.
The agent can still perform routine work automatically. Human involvement is added only where a decision requires review.
For example, an agent could:
Read a customer request.
Analyze the request.
Prepare a response.
Determine that a refund may be required.
Pause for approval.
Continue only after an authorized employee approves the refund.
This gives the agent automation while keeping a human responsible for sensitive decisions.
Why Long-Running Agents Need Approval Workflows
Human approval becomes more important when an agent can run for an extended period.
Consider an employee onboarding agent:
Read Employee Information
|
v
Validate Documents
|
v
Create Account Request
|
v
Human Approval
|
v
Create Accounts
|
v
Send Welcome NotificationThe first three steps might be completely automated.
Creating accounts, however, may require authorization.
If the agent reaches the approval step and the application process is restarted, the workflow should not forget that it was waiting for a decision.
The state needs to represent something like:
Workflow Status: WaitingForApproval
Approval Status: PendingThe workflow can then continue later without starting the entire operation again.
Representing Approval State in C#
A simple approval model can be represented with an enum:
public enum ApprovalStatus
{
Pending,
Approved,
Rejected
}The workflow state can then contain the approval information:
public sealed class ApprovalState
{
public string WorkflowId { get; set; } = string.Empty;
public ApprovalStatus Status { get; set; }
public string? ApprovedBy { get; set; }
public DateTimeOffset? DecisionTime { get; set; }
public string? Reason { get; set; }
}This state provides enough information to understand what happened.
For example:
WorkflowId: WF-1024
Status: Approved
ApprovedBy: employee-123
DecisionTime: 2026-09-25T10:30:00ZIn a real application, the workflow state would normally be persisted rather than kept only in application memory.
Separating Agent Decisions From Human Decisions
One of the most important design principles is to clearly separate what the agent decides from what the human approves.
For example:
Agent:
"Refund of $750 is recommended because the order
matches the refund policy."
Human:
"Approve"
System:
"Execute refund."The agent recommends an action.
The human authorizes the action.
The application executes it.
This separation makes the workflow easier to understand and audit.
A C# model could represent the proposed action like this:
public sealed class ProposedAction
{
public string ActionId { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public decimal Amount { get; set; }
public string Reason { get; set; } = string.Empty;
public ApprovalStatus ApprovalStatus { get; set; }
}The agent can populate the proposal, while the approval process controls whether the action can actually execute.
Building the Workflow Around an Approval Point
A long-running workflow can be structured into separate steps:
public async Task ProcessRequestAsync(
Request request,
CancellationToken cancellationToken)
{
var analysis = await AnalyzeRequestAsync(
request,
cancellationToken);
var action = await CreateProposalAsync(
analysis,
cancellationToken);
await SaveProposalAsync(
action,
cancellationToken);
// Workflow pauses here until approval.
if (action.ApprovalStatus != ApprovalStatus.Approved)
{
return;
}
await ExecuteActionAsync(
action,
cancellationToken);
}The important part is not the if statement itself.
The important part is that the approval becomes part of the workflow state.
A durable workflow can stop after creating the proposal and continue after an approval event is received.
Waiting for Human Input
A common mistake is to keep the application process running while waiting for someone to approve an action.
For example:
while (!approved)
{
await Task.Delay(TimeSpan.FromMinutes(1));
}This approach is generally not suitable for long-running workflows.
It keeps a process alive simply to wait for an external decision.
A better design is event-driven:
Agent
|
v
Create Approval Request
|
v
Persist Workflow State
|
v
Wait
|
| Approval Event
v
Resume Workflow
|
v
Execute ActionThe workflow does not need to continuously poll for approval.
Instead, the approval system can generate an event when the human makes a decision.
Approval Events in C#
You can represent an approval decision as a domain event:
public sealed class ApprovalDecision
{
public string WorkflowId { get; init; } = string.Empty;
public ApprovalStatus Status { get; init; }
public string ApproverId { get; init; } = string.Empty;
public string? Reason { get; init; }
}When the user approves the request, the application can process the event:
public async Task HandleApprovalAsync(
ApprovalDecision decision,
CancellationToken cancellationToken)
{
var workflow = await workflowStore.GetAsync(
decision.WorkflowId,
cancellationToken);
if (workflow is null)
{
throw new InvalidOperationException(
"Workflow was not found.");
}
workflow.ApprovalStatus = decision.Status;
workflow.ApproverId = decision.ApproverId;
workflow.ApprovalReason = decision.Reason;
await workflowStore.SaveAsync(
workflow,
cancellationToken);
}The workflow can then resume from the persisted state.
Approval Is Not the Same as Authentication
Another important distinction is between authentication and authorization.
Authentication answers:
Who is this person?Authorization answers:
Is this person allowed to approve this action?For example, simply knowing that a user is logged in does not mean that the user should be allowed to approve a $50,000 transaction.
Approval workflows should therefore integrate with the application's authorization rules.
Conceptually:
User Identity
|
v
Authentication
|
v
Authorization
|
v
Approval Permission
|
v
Approve / RejectThe exact authorization rules depend on the application and business requirements.
Handling Approval Expiration
An approval request should not necessarily remain valid forever.
For example:
public sealed class ApprovalRequest
{
public string Id { get; init; } = string.Empty;
public string WorkflowId { get; init; } = string.Empty;
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset ExpiresAt { get; init; }
public ApprovalStatus Status { get; set; }
}The workflow can check whether the request has expired:
if (DateTimeOffset.UtcNow >= approval.ExpiresAt)
{
approval.Status = ApprovalStatus.Rejected;
}This is useful when the underlying information can become outdated.
For example, an approval based on a price, inventory level, or security assessment may no longer be valid after a certain period.
Handling Rejection
Rejection should be treated as a valid workflow outcome, not necessarily as an application error.
For example:
Agent Analysis
|
v
Approval Request
|
+---- Approved ----> Execute
|
+---- Rejected ----> Record Decision
|
v
CompleteThe application should store the reason when appropriate:
if (decision.Status == ApprovalStatus.Rejected)
{
workflow.Status = "Rejected";
workflow.RejectionReason = decision.Reason;
}This helps users understand why an action did not happen and provides useful information for auditing.
Comparing Automatic and Human-Assisted Agents
Area | Fully Automatic Agent | Human-in-the-Loop Agent |
|---|---|---|
Routine decisions | Fully automated | Can remain automated |
Sensitive decisions | Agent decides | Human can approve |
Long-running workflow | Requires state management | Requires state plus approval state |
Risk control | Depends heavily on agent behavior | Adds a human checkpoint |
Auditability | Requires detailed logging | Approval decisions can be recorded |
User involvement | Minimal | Required at defined points |
Workflow complexity | Lower initially | Higher, but more controlled |
Human approval does not mean every action should require manual intervention.
The goal is to identify the points where human judgment adds meaningful value.
Common Mistakes
Keeping the Process Running While Waiting
Do not keep a worker alive indefinitely while waiting for approval.
Persist the state and resume when the decision arrives.
Allowing Approval Without Authorization
Do not assume every authenticated user can approve every action.
Apply appropriate authorization rules.
Losing the Approval Context
The approver should be able to understand what they are approving.
Store the relevant proposal, reasoning, affected resource, and other required context.
Executing an Action Twice
A workflow may receive duplicate approval events.
The execution step should therefore be designed to handle duplicate messages safely.
Treating Rejection as an Exception
A rejection is often a normal business outcome.
Model it explicitly rather than treating every rejection as a technical failure.
Best Practices for Production
Persist the Approval State
Store:
Workflow ID
Approval request ID
Current status
Proposed action
Approver identity
Decision timestamp
Approval or rejection reason
Use Idempotency
Approval events can potentially be delivered more than once.
Use a unique approval or workflow identifier to prevent duplicate execution.
Keep an Audit Trail
Record important approval events:
Approval Created
Approval Viewed
Approval Approved
Approval Rejected
Workflow Resumed
Action ExecutedThe exact audit requirements depend on the application.
Show Clear Approval Information
A human should not have to inspect raw agent output to understand the decision.
Present the important information clearly:
Action:
Issue customer refund
Amount:
$750
Reason:
Request meets refund policy
Status:
Pending ApprovalKeep Human Decisions Explicit
Do not let the agent interpret an ambiguous response as approval.
Use explicit states such as:
Approved
Rejected
Pending
ExpiredProtect Sensitive Information
Approval interfaces may expose customer, financial, operational, or other sensitive information.
Only expose the information required for the approval decision.
Advantages and Disadvantages
Advantages
Adds human oversight to sensitive agent actions
Supports long-running workflows
Creates a clear approval trail
Reduces the need for fully autonomous decisions
Makes business rules easier to enforce
Allows routine work to remain automated
Disadvantages
Introduces waiting time into the workflow
Requires persistent approval state
Adds authorization and audit requirements
Requires additional UI or notification mechanisms
Makes workflow design more complex
Troubleshooting Human Approval Workflows
When an agent does not resume after approval, check the following:
Verify that the approval event contains the correct workflow ID.
Check whether the approval state was persisted.
Confirm that the approver had the required permission.
Check whether the approval request expired.
Verify that duplicate-event handling is working.
Check whether the workflow was actually resumed after the decision.
Review logs for the approval request and workflow identifiers.
Confirm that the execution step did not already run.
These checks help separate workflow problems from authorization, persistence, or messaging problems.
Summary
Human approval provides a practical way to combine AI automation with human control in long-running C# agent workflows.
The agent can analyze information and prepare an action, while a human reviews sensitive decisions before the workflow continues. The approval itself should become part of the durable workflow state rather than being treated as a temporary application variable.
For production systems, focus on persistent state, explicit approval statuses, authorization, idempotency, expiration, audit logging, and event-driven workflow resumption.
The most useful design is usually not a completely autonomous agent or a workflow that requires human approval at every step. Instead, let the agent handle routine work and introduce human approval at the points where business rules, risk, or human judgment require it.

Join the conversation! Your thoughts help the community grow.