Closing Genesys Cloud Agent Assist Sessions Programmatically with C#
What You Will Build
- A production-ready C# service that closes Genesys Cloud Agent Assist sessions by constructing validated close payloads containing session identifiers, structured summary matrices, and archive directives.
- This implementation uses the Genesys Cloud Platform Client V2 SDK for C# and targets the
/api/v2/agent-assist/sessions/{sessionId}/closeendpoint with atomic POST operations. - The tutorial covers C# (.NET 8) with async/await patterns, exponential backoff retry logic, webhook synchronization handlers, and structured audit logging for governance compliance.
Prerequisites
- OAuth2 Client Credentials grant configured in Genesys Cloud Admin with
agent-assist:session:writeandwebhook:writescopes. - Genesys Cloud Platform Client V2 SDK v8.0+ installed via NuGet package
GenesysCloudPlatform.Client.V2. - .NET 8 SDK or later runtime environment.
- External dependencies:
Microsoft.Extensions.Logging,System.Text.Json,Polly(for resilient HTTP execution).
Authentication Setup
Genesys Cloud requires a bearer token for all Agent Assist API operations. The following implementation uses the Client Credentials flow to acquire and cache tokens. The token manager handles expiration tracking and automatic refresh to prevent mid-operation 401 responses.
using System.Net.Http.Headers;
using System.Text.Json;
public class OAuthTokenManager
{
private readonly HttpClient _httpClient;
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _baseUrl = "https://api.mypurecloud.com";
private string _accessToken;
private DateTime _expiresAt;
public OAuthTokenManager(HttpClient httpClient, string clientId, string clientSecret)
{
_httpClient = httpClient;
_clientId = clientId;
_clientSecret = clientSecret;
}
public async Task<string> GetAccessTokenAsync()
{
if (_accessToken != null && DateTime.UtcNow.AddSeconds(60) < _expiresAt)
{
return _accessToken;
}
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", _clientId),
new KeyValuePair<string, string>("client_secret", _clientSecret),
new KeyValuePair<string, string>("scope", "agent-assist:session:write webhook:write")
});
var response = await _httpClient.PostAsync($"{_baseUrl}/oauth/token", content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var tokenData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(json);
_accessToken = tokenData["access_token"].GetString();
var expiresIn = tokenData["expires_in"].GetInt32();
_expiresAt = DateTime.UtcNow.AddSeconds(expiresIn);
return _accessToken;
}
}
The OAuth endpoint returns a JSON payload containing the bearer token and expiration window. The cache window includes a 60-second safety buffer to prevent race conditions during high-throughput closing operations.
Implementation
Step 1: Construct and Validate Close Payloads
The Agent Assist engine enforces strict schema constraints on session closure. You must validate summary length, archive directives, and knowledge link structures before transmission. The following method constructs the CloseSessionRequest model and enforces platform limits.
using GenesysCloudPlatform.Client.V2.Model;
using System.Text.Json;
public class SessionClosePayloadBuilder
{
private const int MaxSummaryLength = 4000;
private const int MaxKnowledgeLinks = 10;
public CloseSessionRequest BuildAndValidate(
string sessionId,
string summary,
bool archive,
List<KnowledgeLink> knowledgeLinks,
ActionItem[] actionItems,
SentimentAnalysis sentiment)
{
if (summary.Length > MaxSummaryLength)
{
throw new ArgumentException($"Summary exceeds maximum length of {MaxSummaryLength} characters.");
}
if (knowledgeLinks.Count > MaxKnowledgeLinks)
{
throw new ArgumentException($"Knowledge links exceed maximum count of {MaxKnowledgeLinks}.");
}
var closeRequest = new CloseSessionRequest
{
Summary = summary,
Archive = archive,
KnowledgeLinks = knowledgeLinks,
ActionItems = actionItems,
SentimentAnalysis = sentiment
};
return closeRequest;
}
}
The CloseSessionRequest object maps directly to the JSON schema expected by the Genesys Cloud API. The validation step prevents 400 Bad Request responses caused by payload size violations or structural mismatches. The platform rejects summaries that exceed 4000 characters and limits knowledge link arrays to ten entries per closure request.
Step 2: Execute Atomic POST with Retry and Format Verification
Session closure is an atomic operation. The API returns a 204 No Content response on success. You must implement retry logic for 429 Too Many Requests responses and handle 5xx server errors gracefully. The following method uses Polly to configure resilient execution.
using GenesysCloudPlatform.Client.V2.Api;
using Polly;
using Polly.Extensions.Http;
public class AgentAssistSessionCloser
{
private readonly AgentAssistApi _api;
private readonly SessionClosePayloadBuilder _builder;
private readonly ILogger<AgentAssistSessionCloser> _logger;
public AgentAssistSessionCloser(AgentAssistApi api, SessionClosePayloadBuilder builder, ILogger<AgentAssistSessionCloser> logger)
{
_api = api;
_builder = builder;
_logger = logger;
}
public async Task<CloseOperationResult> CloseSessionAsync(
string sessionId,
string summary,
bool archive,
List<KnowledgeLink> knowledgeLinks,
ActionItem[] actionItems,
SentimentAnalysis sentiment)
{
var closeRequest = _builder.BuildAndValidate(sessionId, summary, archive, knowledgeLinks, actionItems, sentiment);
var policy = HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
try
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
await policy.ExecuteAsync(async () =>
{
await _api.PostAgentAssistSessionCloseAsync(sessionId, closeRequest);
});
stopwatch.Stop();
_logger.LogInformation("Session {SessionId} closed successfully in {Latency}ms.", sessionId, stopwatch.ElapsedMilliseconds);
return new CloseOperationResult
{
SessionId = sessionId,
Success = true,
LatencyMs = stopwatch.ElapsedMilliseconds,
Timestamp = DateTime.UtcNow
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to close session {SessionId}.", sessionId);
return new CloseOperationResult
{
SessionId = sessionId,
Success = false,
Error = ex.Message,
Timestamp = DateTime.UtcNow
};
}
}
}
public record CloseOperationResult(
string SessionId,
bool Success,
long LatencyMs,
string Error,
DateTime Timestamp);
The HTTP request cycle follows this pattern:
- Method:
POST - Path:
/api/v2/agent-assist/sessions/{sessionId}/close - Headers:
Authorization: Bearer <token>,Content-Type: application/json - Request Body: The serialized
CloseSessionRequestobject containing summary, archive flag, knowledge links, action items, and sentiment data. - Response:
204 No Contenton success. The platform automatically triggers knowledge link associations and archives the session based on the directive.
The retry policy handles 429 rate limits and 5xx server errors. The exponential backoff prevents cascading failures during high-volume wrap-up windows. The latency measurement feeds directly into wrap-up success rate calculations.
Step 3: Process Webhooks, Audit Logs, and Wrap-Up Metrics
Genesys Cloud emits an agent-assist.session.closed webhook when the engine completes the closure transaction. You must expose an HTTP endpoint to receive these events, verify the payload structure, and synchronize with external case management systems. The following handler processes incoming webhooks and maintains audit trails.
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
[ApiController]
[Route("api/webhooks")]
public class AgentAssistWebhookController : ControllerBase
{
private readonly ILogger<AgentAssistWebhookController> _logger;
private readonly ConcurrentDictionary<string, CloseOperationResult> _metricsStore = new();
public AgentAssistWebhookController(ILogger<AgentAssistWebhookController> logger)
{
_logger = logger;
}
[HttpPost("agent-assist/closed")]
public IActionResult HandleSessionClosed([FromBody] SessionClosedWebhookPayload payload)
{
if (payload == null || string.IsNullOrWhiteSpace(payload.SessionId))
{
return BadRequest("Invalid webhook payload.");
}
_logger.LogInformation("Received close webhook for session {SessionId} at {Timestamp}.",
payload.SessionId, payload.Timestamp);
var operationResult = _metricsStore.TryGetValue(payload.SessionId, out var existing)
? existing
: new CloseOperationResult(payload.SessionId, true, 0, null, DateTime.UtcNow);
var auditLog = new
{
Event = "agent-assist.session.closed",
SessionId = payload.SessionId,
SummaryLength = payload.Summary?.Length ?? 0,
ArchiveStatus = payload.Archive,
KnowledgeLinksCount = payload.KnowledgeLinks?.Count ?? 0,
ActionItemsCount = payload.ActionItems?.Length ?? 0,
Sentiment = payload.SentimentAnalysis?.Overall,
ProcessedAt = DateTime.UtcNow
};
_logger.LogDebug("Audit log generated: {AuditData}", JsonSerializer.Serialize(auditLog));
return Ok();
}
}
public record SessionClosedWebhookPayload(
string SessionId,
string Summary,
bool Archive,
List<KnowledgeLink> KnowledgeLinks,
ActionItem[] ActionItems,
SentimentAnalysis SentimentAnalysis,
DateTime Timestamp);
The webhook handler validates the incoming JSON against the expected schema. It extracts action item counts, sentiment classifications, and archive status for governance reporting. The audit log structure enables downstream compliance pipelines to verify data capture completeness. Wrap-up success rates are calculated by comparing successful 204 responses against total closure attempts logged in the metrics store.
Complete Working Example
The following console application demonstrates the full lifecycle from token acquisition to session closure and webhook synchronization. Replace the placeholder credentials with your Genesys Cloud OAuth application values.
using GenesysCloudPlatform.Client.V2;
using GenesysCloudPlatform.Client.V2.Api;
using GenesysCloudPlatform.Client.V2.Model;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Net.Http.Headers;
class Program
{
static async Task Main(string[] args)
{
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Debug));
services.AddSingleton(sp =>
{
var client = new HttpClient { BaseAddress = new Uri("https://api.mypurecloud.com/") };
return new OAuthTokenManager(client, "YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET");
});
services.AddSingleton<SessionClosePayloadBuilder>();
services.AddSingleton<AgentAssistSessionCloser>();
var provider = services.BuildServiceProvider();
var logger = provider.GetRequiredService<ILogger<Program>>();
var tokenManager = provider.GetRequiredService<OAuthTokenManager>();
var closer = provider.GetRequiredService<AgentAssistSessionCloser>();
try
{
var token = await tokenManager.GetAccessTokenAsync();
logger.LogInformation("OAuth token acquired successfully.");
var config = new Configuration
{
AccessToken = token,
BaseUri = "https://api.mypurecloud.com"
};
var api = new AgentAssistApi(config);
var builder = new SessionClosePayloadBuilder();
var sessionCloser = new AgentAssistSessionCloser(api, builder, logger);
var knowledgeLink = new KnowledgeLink { Id = "kb-12345", Title = "Refund Policy v2" };
var actionItem = new ActionItem { Description = "Process refund", Status = "pending" };
var sentiment = new SentimentAnalysis { Overall = "positive", Confidence = 0.89 };
var result = await sessionCloser.CloseSessionAsync(
sessionId: "session-uuid-12345",
summary: "Customer requested refund for delayed shipment. Verified order status. Initiated refund process per policy.",
archive: true,
knowledgeLinks: new List<KnowledgeLink> { knowledgeLink },
actionItems: new[] { actionItem },
sentiment: sentiment);
if (result.Success)
{
logger.LogInformation("Wrap-up complete. Latency: {Latency}ms. Success Rate: {Rate}%",
result.LatencyMs, 100.0);
}
else
{
logger.LogError("Wrap-up failed: {Error}", result.Error);
}
}
catch (Exception ex)
{
logger.LogError(ex, "Fatal error during session closure workflow.");
}
}
}
Common Errors & Debugging
Error: 400 Bad Request
- What causes it: The close payload violates schema constraints. Common triggers include summary strings exceeding 4000 characters, malformed knowledge link objects, or missing required fields in the action item array.
- How to fix it: Validate payload length and structure before transmission. Use the
SessionClosePayloadBuildervalidation method to catch violations locally. Inspect the API response body for specific field error codes. - Code showing the fix: The
BuildAndValidatemethod enforcesMaxSummaryLengthandMaxKnowledgeLinksthresholds, throwing descriptive exceptions before the HTTP call executes.
Error: 401 Unauthorized or 403 Forbidden
- What causes it: The OAuth token has expired, lacks the
agent-assist:session:writescope, or the client application lacks permission to modify Agent Assist sessions. - How to fix it: Regenerate the token using the
OAuthTokenManager. Verify the OAuth application in Genesys Cloud Admin includes the correct scopes. Ensure the token is attached to theConfigurationobject before SDK initialization. - Code showing the fix: The
GetAccessTokenAsyncmethod checks expiration and refreshes the token automatically. The scope string explicitly requestsagent-assist:session:write.
Error: 429 Too Many Requests
- What causes it: The Agent Assist engine enforces rate limits per tenant and per OAuth client. High-volume wrap-up windows trigger throttling.
- How to fix it: Implement exponential backoff retry logic. The Polly policy in
CloseSessionAsynchandles 429 responses by retrying up to three times with doubling intervals. - Code showing the fix:
HttpPolicyExtensions.HandleTransientHttpError().OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests).WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))ensures safe iteration without overwhelming the API.
Error: 503 Service Unavailable
- What causes it: The Agent Assist engine is undergoing maintenance, scaling operations, or experiencing backend dependency failures.
- How to fix it: Retry with increased backoff intervals. Monitor Genesys Cloud status pages for engineering incidents. Log the failure for audit tracking and queue the session for asynchronous retry.
- Code showing the fix: The Polly transient error handler includes 5xx status codes. The
CloseOperationResultcaptures the failure state for downstream compensation workflows.