Merging a pull request sounds like a simple operation. A developer reviews the changes, checks the status, and clicks Merge.
That workflow becomes less simple when a team manages hundreds or thousands of pull requests through automation.
A deployment platform may need to merge a pull request after tests pass. An internal engineering portal may need to process several repository operations in the background. A GitHub App may also need to continue doing other work instead of keeping a request open while GitHub completes the merge.
This is where GitHub's asynchronous merge capability becomes useful.
Instead of treating a merge as a request that must complete before your application can continue, an application can submit the merge operation and handle its completion asynchronously.
For developers building GitHub integrations, this changes how merge automation should be designed.
What Is an Async Merge?
A normal synchronous workflow looks like this:
Application
|
| Merge Pull Request
v
GitHub
|
| Wait
v
Merge Completed
|
v
Application ContinuesThe application sends the request and waits for the result.
An asynchronous workflow is different:
Application
|
| Request Merge
v
GitHub
|
v
Merge Queued / Processing
|
+--------------------+
|
Application continues |
|
v
Merge CompletedThe application does not need to keep a request waiting while the operation is processed.
This is especially useful for systems that already use background jobs, queues, webhooks, or event-driven workflows.
Why Does Asynchronous Merging Matter?
The biggest benefit is that your application does not need to treat every GitHub operation as an immediate request-response transaction.
Imagine an internal deployment system:
Pull Request
|
v
Build
|
v
Tests
|
v
Security Checks
|
v
Request Merge
|
v
Deployment WorkflowIf the application waits synchronously for every merge operation, a large number of concurrent operations can consume application resources.
With asynchronous processing, the application can submit the operation and continue managing other work.
This fits naturally with modern distributed systems.
A Typical Async Merge Architecture
A production application can use a queue-based design.
Developer
|
v
Internal Portal
|
v
Merge Request Created
|
v
Message Queue
|
v
Merge Worker
|
v
GitHub
|
v
Merge Result / Event
|
v
Application DatabaseThe web application accepts the user's request quickly.
A background worker then performs the GitHub operation.
This separation has another advantage: temporary GitHub failures do not necessarily have to affect the user-facing application immediately.
When Should You Use Async Merge?
Asynchronous merging makes sense when the merge operation is part of a larger workflow.
Common examples include:
Automated release pipelines
Repository maintenance tools
Internal developer portals
Large-scale repository automation
GitHub Apps
Automated dependency update workflows
Organization-wide engineering platforms
For a developer manually merging one pull request from GitHub's interface, there is usually no reason to build an asynchronous workflow.
The value appears when your application is responsible for managing many operations.
Example: A .NET Merge Service
A clean implementation should keep GitHub communication inside a dedicated service.
For example:
public sealed class PullRequestMergeService
{
private readonly HttpClient _httpClient;
public PullRequestMergeService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task RequestMergeAsync(
string owner,
string repository,
int pullRequestNumber,
CancellationToken cancellationToken)
{
// Build the GitHub merge request here.
// Authentication and request details
// should be configured by the application.
await Task.CompletedTask;
}
}The important design choice is separation.
Your controller should not contain all GitHub API logic.
Instead:
Controller
|
v
Merge Service
|
v
GitHub API ClientThis makes the integration easier to test and maintain.
Using a Background Worker
In ASP.NET Core, a background worker can process merge jobs separately from HTTP requests.
A simplified example looks like this:
public sealed class MergeWorker : BackgroundService
{
private readonly ILogger<MergeWorker> _logger;
public MergeWorker(
ILogger<MergeWorker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// Read a merge job from the queue.
// Validate the repository and pull request.
// Request the merge operation.
_logger.LogInformation(
"Processed merge request.");
await Task.Delay(
TimeSpan.FromSeconds(5),
stoppingToken);
}
}
}A real implementation should use a proper queue instead of Task.Delay, but the example shows the basic architecture.
The HTTP request and GitHub operation are no longer tightly coupled.
Why a Queue Helps
Suppose 500 pull requests become eligible for merging at approximately the same time.
A web application should not necessarily create 500 simultaneous API operations.
A queue can smooth out the workload:
500 Merge Requests
|
v
Queue
|
v
+-------+-------+
| | |
Worker Worker Worker
| | |
+-------+-------+
|
v
GitHubWorkers can process jobs according to the application's concurrency limits.
This also makes retries easier.
Idempotency Matters
Distributed systems can retry operations.
For example:
Request
|
v
GitHub
|
| Timeout
X
Application does not know result
|
v
RetryThe dangerous assumption is:
The timeout means the merge did not happen.
That is not necessarily true.
The request may have reached GitHub successfully, while the response was lost because of a network problem.
Before retrying, your application should determine the current state of the pull request whenever possible.
A useful state model might be:
public enum MergeJobStatus
{
Pending,
Processing,
Completed,
Failed,
Unknown
}The Unknown state is useful because distributed systems do sometimes produce ambiguous outcomes.
Do not automatically convert every timeout into Failed.
Checking Pull Request State
Before submitting a merge operation, your worker should validate the pull request.
For example, it may need to confirm:
The pull request still exists.
The pull request is still open.
Required checks have passed.
The repository is accessible.
The merge strategy is valid.
The application still has permission.
A simplified validation method could look like this:
public async Task<bool> CanMergeAsync(
PullRequestInfo pullRequest,
CancellationToken cancellationToken)
{
if (!pullRequest.IsOpen)
{
return false;
}
if (!pullRequest.RequiredChecksPassed)
{
return false;
}
return true;
}The actual conditions should match the organization's merge policy.
Do Not Treat Merge as the End of the Workflow
A common design mistake is assuming that once GitHub accepts a merge request, the entire workflow is finished.
In many engineering systems, merging starts another sequence:
Merge Requested
|
v
Merge Completed
|
v
Build
|
v
Package
|
v
Deploy
|
v
VerifyYour application should therefore model the merge as one state transition within a larger workflow.
For example:
Pull Request
|
v
Approved
|
v
Merge Requested
|
v
Merged
|
v
Deployment StartedThis makes the system easier to reason about.
Error Handling
External GitHub operations can fail for many reasons.
Your application should distinguish between permanent and temporary failures.
For example:
Failure | Typical Response |
|---|---|
Authentication failure | Fix credentials |
Permission failure | Check GitHub App permissions |
Invalid pull request | Mark job failed |
Pull request already merged | Treat as completed |
Temporary network failure | Retry |
Rate limiting | Back off and retry |
Merge conflict | Require another action |
Required check failed | Wait or stop |
This distinction prevents unnecessary retries.
For example, retrying an invalid pull request 10 times will not fix the problem.
Retry With Backoff
Transient failures should normally use controlled retries.
A simple exponential backoff can look like:
private static TimeSpan GetRetryDelay(int attempt)
{
var seconds = Math.Pow(2, attempt);
return TimeSpan.FromSeconds(
Math.Min(seconds, 60));
}This produces increasingly longer delays while placing an upper limit on the wait.
Production systems should also use a maximum retry count.
For example:
Attempt 1
|
v
Wait 2 seconds
|
Attempt 2
|
v
Wait 4 seconds
|
Attempt 3
|
v
Wait 8 secondsDo not retry indefinitely.
Authentication and Security
Merge operations are more sensitive than read-only repository operations.
A tool that can merge code can potentially change production software.
For this reason, authentication should be designed carefully.
Keep credentials outside source code.
For example:
var token =
Environment.GetEnvironmentVariable("GITHUB_TOKEN");
if (string.IsNullOrWhiteSpace(token))
{
throw new InvalidOperationException(
"GitHub authentication is not configured.");
}In production, credentials should normally come from an appropriate secret-management mechanism.
The application should also use the smallest practical permissions.
A merge automation service does not automatically need administrative access to every repository.
Common Mistakes
Waiting for Every Operation
If the purpose of the workflow is asynchronous processing, do not turn the worker into another synchronous request chain.
Submit the operation, track its state, and process completion separately.
Ignoring Duplicate Requests
A developer or automation system might accidentally submit the same merge job more than once.
Use a unique job identifier and track its state.
Retrying Every Error
Not every error is temporary.
A permission failure will not normally be fixed by sending the same request again.
Assuming Timeout Means Failure
A timeout means your application did not receive the expected response.
It does not always mean GitHub did not process the operation.
Skipping State Validation
The pull request may have changed between the time it was placed in the queue and the time the worker processes it.
Always validate current state before taking a sensitive action.
Troubleshooting Async Merge Workflows
Jobs Remain Pending
Check the worker process first.
Then verify:
The queue is receiving messages.
Workers are running.
Authentication is valid.
The repository is accessible.
The worker is not blocked by rate limits.
Merge Requests Fail Repeatedly
Look at the actual GitHub response rather than retrying automatically.
Check permissions, repository state, required checks, and merge conflicts.
The Database Says Failed but the Pull Request Is Merged
This usually indicates an ambiguous distributed operation.
The merge may have completed while the application failed to receive or process the response.
The correct solution is to reconcile the application's state with GitHub's current state.
Advantages
Better Scalability
Background workers can process many merge requests without tying them directly to user-facing HTTP requests.
Cleaner Architecture
Queues, workers, and state tracking fit naturally into distributed application architectures.
Better Failure Handling
Jobs can be retried or moved to a failed state without blocking the main application.
Useful for Large-Scale Automation
Organizations managing many repositories can build centralized merge workflows.
Disadvantages
More Infrastructure
A proper asynchronous workflow may require queues, workers, persistence, monitoring, and retry logic.
More Complex State Management
Your application has to track states such as pending, processing, completed, failed, and unknown.
Eventual Consistency
The application's database and GitHub may temporarily disagree about the current state.
More Care Is Needed for Sensitive Operations
Automated merging can affect production code, so permissions and approval policies must be carefully designed.
Best Practices
Use asynchronous processing for long-running or high-volume workflows.
Keep GitHub API calls in a dedicated service layer.
Use a durable queue for production workloads.
Track every merge job with a unique identifier.
Validate the pull request immediately before the merge operation.
Handle timeouts as ambiguous results rather than automatic failures.
Use bounded retries with exponential backoff.
Store enough logs to trace each merge request.
Use least-privilege authentication.
Keep human approval for workflows where automatic merging could create unacceptable risk.
Summary
GitHub Async Merge API enables developers to build merge workflows that do not have to remain blocked while a merge operation is processed.
It is particularly useful for GitHub Apps, CI/CD systems, internal developer portals, and large-scale repository automation. A reliable implementation should use background workers, durable job tracking, proper authentication, bounded retries, state validation, and careful error handling.
The API itself is only one part of the solution. The real engineering work is building a workflow that remains reliable when networks fail, pull requests change, requests are duplicated, or the result of an operation is temporarily unknown.

Join the conversation! Your thoughts help the community grow.