Platform API Client SDK - .NET [![NuGet Badge](https://img.shields.io/nuget/v/PureCloudPlatform.Client.V2)](https://www.nuget.org/packages/PureCloudPlatform.Client.V2/) [![Release Notes Badge](https://developer-content.genesys.cloud/images/sdk-release-notes.png)](https://github.com/MyPureCloud/platform-client-sdk-dotnet/blob/master/releaseNotes.md) Documentation can be found at https://mypurecloud.github.io/platform-client-sdk-dotnet/ Documentation version PureCloudPlatform.Client.V2 272.0.0 ## Install Using nuget ```bash install-package PureCloudPlatform.Client.V2 ``` Package info can be found at [https://www.nuget.org/packages/PureCloudPlatform.Client.V2/](https://www.nuget.org/packages/PureCloudPlatform.Client.V2/) ## Using the Library **Warning:** This library is generated using the Genesys Cloud public API swagger definition. Function parameter ordering can change without notice and cause breaking changes. To avoid this, it is recommended to use [named arguments](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments#named-arguments) when making API requests. ## Preview APIs **Warning:** Preview APIs are included in this SDK. These resources are subject to both breaking and non-breaking changes at any time without notice. This includes, but is not limited to, changing resource names, paths, contracts, documentation, and removing resources entirely. For a full list of the preview APIs see [here](https://developer.genesys.cloud/platform/preview-apis) ### Referencing the Library If you've used the [Package Manager Console](https://docs.nuget.org/consume/package-manager-console) to install the package, there are no additional steps. If you're building from source or otherwise not using nuget, reference your version of PureCloudPlatform.Client.V2.dll in your project and add references or install packages for [RestSharp](http://www.nuget.org/packages/RestSharp/) and [JSON.NET](http://www.nuget.org/packages/Newtonsoft.Json/). ### Authenticating #### Implicit Grant **Use when...** * The app is authenticating as a human * The app is running locally on the user's computer * The app has an embedded browser to use for OAuth If the application will be authenticating as a human, the [Implicit Grant](https://developer.genesys.cloud/authorization/platform-auth/use-implicit-grant) OAuth 2 flow may be used from an embeddable browser. The access token can be retrieved from the querystring of the redirected URL in the browser control. This process is packaged by the [GenesysCloudOAuthWebView](https://github.com/MyPureCloud/oauth-webview-dotnet) project. See the browser control implemented in a winforms project in the [OAuth With Implicit Grant Login Flow](https://developer.genesys.cloud/authorization/platform-auth/guides/oauth-implicit-guide) tutorial. #### Authorization Code Grant **Use when...** * The app is authenticating as a human * The app is served via a web server, such as IIS * There is server-side code that will be making API requests The [Authorization Code Grant](https://developer.genesys.cloud/authorization/platform-auth/use-authorization-code) will return the auth code in the querystring to allow the server-side code to make the request to get an access token with the auth code, and prevent the access token from being known by the client-side. The process for this is: * Redirect user to OAuth login page * When the user is redirected to your URL, retrieve the auth code from the querystring on the server side * On the server side, exchange the auth code for an access token ##### ASP.NET tutorial This is a tutorial of how to use an Authorization Code Grant without using the SDK: [https://developer.genesys.cloud/authorization/platform-auth/guides/oauth-auth-code-guide](https://developer.genesys.cloud/authorization/platform-auth/guides/oauth-auth-code-guide) ##### Example with the SDK In addition to the process in the tutorial above, swap out the POST to `https://login./oauth/token` with the following: Use the following namespaces: ```csharp using PureCloudPlatform.Client.V2.Api; using PureCloudPlatform.Client.V2.Client; using PureCloudPlatform.Client.V2.Extensions; ``` Then call the _PostToken_ extension method of _ApiClient_, including the redirect URI and auth code: ```csharp var accessTokenInfo = Configuration.Default.ApiClient.PostToken("18a4c365-7ea3-4f0g-9fb7-884fb4d2e9c6", "M7FfdYQyL5TA6BdbEZ8M9-Wx4uZai1rNQ7jcuFdcJJo", "http://redirecturi.com/", "6Zxcb0oASMBI55wQJ6bVmOmO57k8CxXBKgzDKtYXbtk"); Console.WriteLine("Access token=" + accessTokenInfo.AccessToken); ``` By default, the SDK will transparently request a new access token using the refresh token when the access token expires. If you wish to implement the refresh logic set _ShouldRefreshAccessToken_ to false and store the refresh token from the auth response: ```csharp var refreshToken = accessTokenInfo.RefreshToken; Configuration.Default.ShouldRefreshAccessToken = false; ``` You can use the _ExpiresIn_ value to determine how long the token will live and proactively request a new one before it expires. ```csharp var tokenTimeToLive = authTokenInfo.ExpiresIn; ``` When the access token expires, refresh it using the _PostToken_ method using the same clientId and clientSecret as used to request it. ```csharp var accessTokenInfo = Configuration.Default.ApiClient.PostToken("18a4c365-7ea3-4f0g-9fb7-884fb4d2e9c6", "M7FfdYQyL5TA6BdbEZ8M9-Wx4uZai1rNQ7jcuFdcJJo", authorizationCode: refreshToken, isRefreshRequest: true); Console.WriteLine("Access token=" + accessTokenInfo.AccessToken); refreshToken = accessTokenInfo.RefreshToken; ``` #### OAuth2 SAML2 Bearer Grant **Use when...** * The app is authenticating as a human user, the [OAuth2 SAML2 Bearer](https://developer.genesys.cloud/authorization/platform-auth/use-saml2-bearer) can be used via the AuthExtensions extension methods. First, use the following namespaces: ```csharp using PureCloudPlatform.Client.V2.Api; using PureCloudPlatform.Client.V2.Client; using PureCloudPlatform.Client.V2.Extensions; ``` Then call the _PostTokenSaml2Bearer_ extension method of _ApiClient_ with your orgName and encodedSamlAssertion ```csharp var accessTokenInfo = Configuration.Default.ApiClient.PostTokenSaml2Bearer("18a4c365-7ea3-4f0g-9fb7-884fb4d2e9c6", "M7FfdYQyL5TA6BdbEZ8M9-Wx4uZai1rNQ7jcuFdcJJo",orgName, encodedSamlAssertion); Console.WriteLine("Access token=" + accessTokenInfo.AccessToken); ``` #### PKCE Grant **Use when...** * The app is authenticating as a human user, the [PKCE Grant](https://developer.genesys.cloud/authorization/platform-auth/use-pkce) can be used via the AuthExtensions extension methods. First, use the following namespaces: ```csharp using PureCloudPlatform.Client.V2.Api; using PureCloudPlatform.Client.V2.Client; using PureCloudPlatform.Client.V2.Extensions; ``` Then call the _PostTokenPKCE_ extension method of _ApiClient_ with your orgName and encodedSamlAssertion ```csharp var accessTokenInfo = Configuration.Default.ApiClient.PostTokenPKCE(clientId, redirectUri, codeVerifier, authCode); Console.WriteLine("Access token=" + accessTokenInfo.AccessToken); ``` The SDK provides methods to generate a PKCE Code Verifier and to compute PKCE Code Challenge. ```csharp codeVerifier = Configuration.Default.ApiClient.GeneratePKCECodeVerifier(128); codeChallenge = Configuration.Default.ApiClient.ComputePKCECodeChallenge(codeVerifier); ``` #### Client Credentials Grant **Use when...** * The app is authenticating as a non-human (e.g. a service, scheduled task, or other non-UI application) For headless and non-user applications, the [Client Credentials Grant](https://developer.genesys.cloud/authorization/platform-auth/use-client-credentials) can be used via the AuthExtensions extension methods. First, use the following namespaces: ```csharp using PureCloudPlatform.Client.V2.Api; using PureCloudPlatform.Client.V2.Client; using PureCloudPlatform.Client.V2.Extensions; ``` Then call the _PostToken_ extension method of _ApiClient_, leaving the redirect URI and auth code blank: ```csharp var accessTokenInfo = Configuration.Default.ApiClient.PostToken("18a4c365-7ea3-4f0g-9fb7-884fb4d2e9c6", "M7FfdYQyL5TA6BdbEZ8M9-Wx4uZai1rNQ7jcuFdcJJo"); Console.WriteLine("Access token=" + accessTokenInfo.AccessToken); ``` ### Making Requests #### Setting the Environment If connecting to a Genesys Cloud environment other than mypurecloud.com (e.g. mypurecloud.ie), set the new base path before constructing any API classes. The new base path should be the base path to the Platform API for your environment. ```csharp PureCloudRegionHosts region = PureCloudRegionHosts.us_east_1; Configuration.Default.ApiClient.setBasePath(region); ``` #### Setting the gateway The Genesys Cloud Login and API URL path can be overridden if necessary (i.e. if the Genesys Cloud requests must be sent through to an intermediate API gateway or equivalent). This can be achieved setting the gateway on the `ApiClient` instance with *SetGateway*. ```csharp Configuration.Default.ApiClient.SetGateway("mygateway.mydomain.myextension", "https", 1443, "myadditionalpathforlogin", "myadditionalpathforapi"); ``` or ```csharp ApiClient.GatewayConfiguration gatewayConfiguration = new ApiClient.GatewayConfiguration(); gatewayConfiguration.Host = "mygateway.mydomain.myextension"; gatewayConfiguration.Protocol = "https"; gatewayConfiguration.Port = 1443; gatewayConfiguration.PathParamsLogin = "myadditionalpathforlogin"; gatewayConfiguration.PathParamsApi = "myadditionalpathforapi"; ... Configuration.Default.ApiClient.GatewayConfig = gatewayConfiguration; ``` * "Host" is the address of your gateway. * "Protocol" is not mandatory. It will default to "https" if the parameter is not defined or empty. * "Port" is not mandatory. This parameter can be defined if a non default port is used and needs to be specified in the url (value must be greater or equal to 0). Set to -1 to use default port (default unspecified port). * "PathParamsLogin" and "PathParamsApi" are not mandatory. They will be appended to the gateway url path if these parameters are defined and non empty (for Login requests and for API requests). * "Username" and "Password" are not used at this stage. This is for a possible future use. With the configuration below, this would result in: * Login requests to: "https://mygateway.mydomain.myextension:1443/myadditionalpathforlogin" (e.g. "https://mygateway.mydomain.myextension:1443/myadditionalpathforlogin/oauth/token") * API requests to: "https://mygateway.mydomain.myextension:1443/myadditionalpathforapi" (e.g. "https://mygateway.mydomain.myextension:1443/myadditionalpathforlogin/api/v2/users/me") #### Setting the max retry time By default, the .NET SDK does not automatically retry any failed requests. To enable automatic retries, provide a RetryConfiguration object with the maximum number of seconds to retry requests and the max number of retries when building the ApiClient instance. Building a `RetryConfiguration` instance: ```csharp var retryConfig = new ApiClient.RetryConfiguration { MaxRetryTimeSec = 10, RetryMax = 5; }; ``` Setting `RetryConfiguration` instance to `ApiClient`: ```csharp Configuration.Default.ApiClient.RetryConfig = retryConfig; ``` Set the `MaxRetryTimeSec` to the number of seconds to process retries before returning an error. Set the `RetryMax` to the retries to attempt before returning an error. When the retry time is a positive integer, the SDK will follow the recommended backoff logic using the provided configuration. The best practices are documented in the [Rate Limiting](https://developer.genesys.cloud/platform/api/rate-limits) Developer Center article. #### Management of HTTP Responses with duplicate header names. When an HTTP Response is received with duplicate header names, the SDK will automatically merge such headers and present them as single entry with a comma separated string value. This applies to response headers in the ApiResponse class, or when enabling SDK logging. e.g. HTTP Response with ("Server": "Google") and ("Server": "FrontEnd") will be made available and logged as: "Server": "Google, FrontEnd" #### SDK Logging Logging of API requests and responses can be controlled by several parameters on the `Configuration`'s `Logger` instance. `LogLevel` values: 1. LogLevel.LTrace (HTTP Method, URL, Request Body, HTTP Status Code, Request Headers, Response Headers) 2. LogLevel.LDebug (HTTP Method, URL, Request Body, HTTP Status Code, Request Headers) 3. LogLevel.LError (HTTP Method, URL, Request Body, Response Body, HTTP Status Code, Request Headers, Response Headers) 4. LogLevel.LNone - default `LogFormat` values: 1. JSON 2. Text - default By default, the request and response bodies are not logged because these can contain PII. Be mindful of this data if choosing to log it. To log to a file, provide a `LogFilePath` value. SDK users are responsible for the rotation of the log file. Example logging configuration: ```csharp Configuration.Default.Logger.Level = LogLevel.LTrace; Configuration.Default.Logger.Format = LogFormat.JSON; Configuration.Default.Logger.LogRequestBody = true; Configuration.Default.Logger.LogResponseBody = true; Configuration.Default.Logger.LogToConsole = true; Configuration.Default.Logger.LogFilePath = "/var/log/dotnetsdk.log"; ``` #### Configuration file Several configuration parameters can be applied using a configuration file. There are two sources for this file: 1. The SDK will look for `%HOMEDRIVE%%HOMEPATH%\.genesysclouddotnet\config` on Windows, or `$HOME/.genesysclouddotnet/config` on Unix. 2. Provide a valid file path to `Configuration.Default.ConfigFilePath` The SDK will take an event-driven approach to monitor for config file changes and will apply changes in near real-time. To disable this behavior, set `Configuration.Default.AutoReloadConfig` to false. INI and JSON formats are supported. See below for examples of configuration values in both formats: INI: ```ini [logging] log_level = trace log_format = text log_to_console = false log_file_path = /var/log/dotnetsdk.log log_response_body = false log_request_body = false [retry] retry_wait_min = 3 retry_wait_max = 10 retry_max = 5 [reauthentication] refresh_access_token = true refresh_token_wait_max = 10 [general] live_reload_config = true host = https://api.mypurecloud.com ``` JSON: ```json { "logging": { "log_level": "trace", "log_format": "text", "log_to_console": false, "log_file_path": "/var/log/dotnetsdk.log", "log_response_body": false, "log_request_body": false }, "retry": { "retry_wait_min": 3, "retry_wait_max": 10, "retry_max": 5 }, "reauthentication": { "refresh_access_token": true, "refresh_token_wait_max": 10 }, "general": { "live_reload_config": true, "host": "https://api.mypurecloud.com" } } ``` The Genesys Cloud Login and API URL path can be overridden if necessary (i.e. if the Genesys Cloud requests must be sent through to an intermediate API gateway or equivalent). This can be achieved defining a "gateway" configuration, in the INI or the JSON configuration file. * "host" is the address of your gateway. * "protocol" is not mandatory. It will default to "https" if the parameter is not defined or empty. * "port" is not mandatory. This parameter can be defined if a non default port is used and needs to be specified in the url (value must be greater or equal to 0). Set to -1 to use default port (default unspecified port). * "path_params_login" and "path_params_api" are not mandatory. They will be appended to the gateway url path if these parameters are defined and non empty (for Login requests and for API requests). * "username" and "password" are not used at this stage. This is for a possible future use. With the configuration below, this would result in: * Login requests to: "https://mygateway.mydomain.myextension:1443/myadditionalpathforlogin" (e.g. "https://mygateway.mydomain.myextension:1443/myadditionalpathforlogin/oauth/token") * API requests to: "https://mygateway.mydomain.myextension:1443/myadditionalpathforapi" (e.g. "https://mygateway.mydomain.myextension:1443/myadditionalpathforlogin/api/v2/users/me") INI: ```ini [logging] log_level = trace log_format = text log_to_console = false log_file_path = /var/log/dotnetsdk.log log_response_body = false log_request_body = false [retry] retry_wait_min = 3 retry_wait_max = 10 retry_max = 5 [reauthentication] refresh_access_token = true refresh_token_wait_max = 10 [general] live_reload_config = true host = https://api.mypurecloud.com [gateway] host = mygateway.mydomain.myextension protocol = https port = 1443 path_params_login = myadditionalpathforlogin path_params_api = myadditionalpathforapi username = username password = password ``` JSON: ```json { "logging": { "log_level": "trace", "log_format": "text", "log_to_console": false, "log_file_path": "/var/log/dotnetsdk.log", "log_response_body": false, "log_request_body": false }, "retry": { "retry_wait_min": 3, "retry_wait_max": 10, "retry_max": 5 }, "reauthentication": { "refresh_access_token": true, "refresh_token_wait_max": 10 }, "general": { "live_reload_config": true, "host": "https://api.mypurecloud.com" }, "gateway": { "host": "mygateway.mydomain.myextension", "protocol": "https", "port": 1443, "path_params_login": "myadditionalpathforlogin", "path_params_api": "myadditionalpathforapi", "username": "username", "password": "password" } } ``` #### Invoking the API There are two steps to making requests: 1. Instantiate one of the API classes in the PureCloudPlatform.Client.V2.Api namespace 2. Call the methods on the API object Example of getting the authenticated user's information: ```csharp // Instantiate instance of the Users API var usersApi = new UsersApi(); // Get the logged in user var me = usersApi.GetMe(); Console.WriteLine($"Hello, {me.DisplayName}"); ``` #### Using multiple ApiClients Applications which need to create and to maintain distinct Configuration and ApiClient instances (i.e. distinct instances to different Genesys Cloud regions or to the same region, with distinct access tokens), can use the optional `useDefaultApiClient` parameter (set to: false - default: true), of the `Configuration` constructor. When the `useDefaultApiClient` optional parameter is not specified or is set to true (`useDefaultApiClient` default value is true), the SDK sets or uses the ApiClient of the Default Configuration (static single instance). **To create and to use distinct Configuration.ApiClient instances, set the `useDefaultApiClient` optional parameter to false.** ```csharp Configuration configuration = new Configuration(useDefaultApiClient: false); ApiClient client = configuration.ApiClient; PureCloudRegionHosts region = PureCloudRegionHosts.eu_west_1; client.setBasePath(region); var apiInstance = new UsersApi(configuration); var userId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; // string | User ID User resultUser = apiInstance.GetUser(userId, null, null, null, null); ``` #### Managing 3xx (Redirect) HTTP Responses The SDK's DefaultHttpClient, based on RestSharp, does not automatically process any 3xx HTTP Responses (issuing a consecutive GET request to the url provided as Response Location Header). This is meant to prevent deserialization errors, when the redirect url provides access to a content which is different from the Platform API operation swagger definition. On 3xx, the SDK will raise an `ApiException.RedirectException` to let the developer catch this reponse and access the url provided in the `Location` HTTP Response Header. The `ApiException.RedirectException` inherits from `ApiException` and can therefore be also managed with an `ApiException`. In order to illustrate this: - The *GET /api/v2/downloads/{downloadId}* endpoint (in the .Net SDK: *GetDownload* method from *DownloadsApi*) is one of the few endpoints which can answer with a 2xx HTTP status or with a 3xx HTTP Status (i.e. Redirect). - Downloading a document, based on the downloadId value, is a two-step process. - First step: The purpose of the *GET /api/v2/downloads/{downloadId}* is to return a specific url where the document is available for download (on a different server). - Second step: You can then query the provided download url to get access to the document and its content. - If you invoke *GET /api/v2/downloads/{downloadId}* endpoint with *issueRedirect = false* (*GetDownload(downloadId, null, false, null)*), the server will answer with a 200 OK and a response body that matches the *UrlResponse* model (which contains the url). - If you invoke *GET /api/v2/downloads/{downloadId}* endpoint with *issueRedirect = true*, or with no *issueRedirect parameter* (*GetDownload(downloadId, null, true, null) or GetDownload(downloadId, null, null, null)*), the server will answer with a *303 See Other* and the download url will be available as a HTTP Response header, named "Location". With *issueRedirect = false*: ```csharp // Instantiate instance of the Downloads API var downloadsApi = new DownloadsApi(); // Get the url of the document for download UrlResponse urlresponse = downloadsApi.GetDownload(downloadId, null, false, null); Console.WriteLine("The download url is: {0}", urlresponse.Url); ``` With unset *issueRedirect* or *issueRedirect = true* (using `ApiException.RedirectException`): ```csharp // Instantiate instance of the Downloads API var downloadsApi = new DownloadsApi(); // Get the url of the document for download try { UrlResponse urlresponse = downloadsApi.GetDownload(downloadId, null, null, null); } catch (ApiException.RedirectException e) { Console.WriteLine("HTTP Redirect Received when calling Platform API Endpoint"); Console.WriteLine("The download url is: {0}", e.Headers["Location"]); } catch (ApiException e) { ... } catch (Exception e) { ... } ``` With unset *issueRedirect* or *issueRedirect = true* (using `ApiException`): ```csharp // Instantiate instance of the Downloads API var downloadsApi = new DownloadsApi(); // Get the url of the document for download try { UrlResponse urlresponse = downloadsApi.GetDownload(downloadId, null, null, null); } catch (ApiException e) { if (e.ErrorCode >= 300 && e.ErrorCode < 400) { Console.WriteLine("HTTP Redirect Received when calling Platform API Endpoint"); Console.WriteLine("The download url is: {0}", e.Headers["Location"]); } else { ... } } catch (Exception e) { ... } ``` Once you have the download url, you can query it issuing a new HTTP request. Here is an example using the *System.Net.Http's HttpClient*: ```csharp var httpClient = new HttpClient(); try { HttpResponseMessage httpResponse = httpClient .GetAsync(downloadUrl) .ConfigureAwait(false) .GetAwaiter() .GetResult(); int statusCode = (int)httpResponse.StatusCode; if (statusCode >= 400 || statusCode == 0) { Console.WriteLine("Error status getting download content: {0}", statusCode); } else { // Successful Response - Read the response content try { var responseBodyByteArray = httpResponse.Content .ReadAsByteArrayAsync() .ConfigureAwait(false) .GetAwaiter() .GetResult(); // Write byte array to a file. Console.WriteLine("Content was downloaded successfully"); // System.IO.File.WriteAllBytes(downloadFilename, responseBodyByteArray); } catch (Exception ex) { Console.WriteLine("Error reading download content"); } } } catch (Exception ex) { Console.WriteLine("Error getting download content"); } ``` #### Managing updates in Platform API Enumerations The Platform API Client SDKs (Java, Javascript/NodeJs, Python, Go, .Net, iOS/Swift) are automatically generated using the Platform API OpenAPI v2 definition. The Platform API definition file is downloaded at the time of the SDK build and is used to generate operations (i.e. API methods) and definitions (i.e. classes for the different models, enumerations, ...) in the different SDK languages. The .Net Platform API Client SDK implements the following strategy to manage the introduction of new enumeration values in the Platform API (i.e. in a version of the SDK which doesn't include these changes): * Each enumeration, generated from the Platform API OpenAPI v2 definition, always contains the following enumeration value: `[EnumMember(Value = "OUTDATED_SDK_VERSION")] OutdatedSdkVersion` * If an unknown enumeration value is received (i.e. a new enumeration value, introduced in Platform API, after the SDK version you are using was built), the SDK will map it to the `[EnumMember(Value = "OUTDATED_SDK_VERSION")] OutdatedSdkVersion` enumeration value. This is to prevent errors during deserialization when an unknown enumeration value is received from Genesys Cloud. ### SDK Specific Types and Classes The Platform API Client SDK for .Net defines some types and classes specific to the SDK. #### YearMonth Some API Endpoints of the Platform API (REST API) contain properties defined as string with a "year-month" format (e.g. value equal to "2026-01"). If part of an API Response, these properties will be deserialized to the SDK's YearMonth class. YearMonth class instance will be serialized to "year-month" string format if part of an API Request (REST API). You can import the YearMonth class in your C# code with: `using PureCloudPlatform.Client.V2.Client;` The YearMonth class is defined in the `PureCloudPlatform.Client.V2.Client` namespace. The YearMonth class contains two properties of integer type: **Year** (allowing value from 0 to 9999) and **Month** (allowing value from 1 to 12). An instance of the class can be created using the class constructor `YearMonth(int? Year = null, int? Month = null)` or parsing a `DateTime` with `YearMonth(DateTime Date)`. A YearMonth instance can also be transformed into a DateTime (with day being forced to 1) with `myYearMonth.ToDate()`. ## NotificationHandler Helper Class The .NET SDK includes a helper class `NotificationHandler` to assist in managing GenesysCloud notifications. The class will create a single notification channel, or use an existing one, and provides methods to add and remove subscriptions and raises an event with a deserialized notification object whenever one is received. **WARNING** The helper uses [WebSocketSharp](https://www.nuget.org/packages/WebSocketSharp)'s websocket implementation. Unfortunately, the package is pre-release only and therefore cannot be included as a dependency in a release package. The dependency must be resolved manually. The nuget page for the package contains instructions for installing the pre-release pacakge. ### Using NotificationHandler Create a new instance: ```csharp var handler = new NotificationHandler(); ``` If you're using a proxy server, use the following constructor: ```csharp var handler = new NotificationHandler("YOUR_PROXY_URL"); ``` If your proxy server requires authentication, use the following constructor: ```csharp var handler = new NotificationHandler("YOUR_PROXY_URL", "YOUR_PROXY_USERNAME", "YOUR_PROXY_PASSWORD"); ``` Add a subscription: ```csharp // Single handler.AddSubscription($"v2.users.{_me.Id}.presence", typeof(PresenceEventUserPresence)); // Multiple var subscriptions = new List>(); subscriptions.Add(new Tuple($"v2.users.{_me.Id}.presence", typeof(PresenceEventUserPresence))); subscriptions.Add(new Tuple($"v2.users.{_me.Id}.routingStatus", typeof(UserRoutingStatusNotification))); handler.AddSubscriptions(subscriptions); ``` Remove a subscription: ```csharp handler.RemoveSubscription($"v2.users.{_me.Id}.conversations"); ``` Handle incoming notification: ```csharp handler.NotificationReceived += (data) => { Console.WriteLine(JsonConvert.SerializeObject(data, Formatting.Indented)); if (data.GetType() == typeof (NotificationData)) { var presence = (NotificationData) data; Console.WriteLine($"New presence: {presence.EventBody.PresenceDefinition.SystemPresence}"); } }; ``` Full example: ```csharp var handler = new NotificationHandler(); handler.AddSubscription($"v2.users.{_me.Id}.presence", typeof(PresenceEventUserPresence)); handler.AddSubscription($"v2.users.{_me.Id}.conversations", typeof(ConversationEventTopicConversation)); handler.NotificationReceived += (data) => { Console.WriteLine(JsonConvert.SerializeObject(data, Formatting.Indented)); if (data.GetType() == typeof (NotificationData)) { var presence = (NotificationData) data; Console.WriteLine($"New presence: {presence.EventBody.PresenceDefinition.SystemPresence}"); } else if (data.GetType() == typeof (NotificationData)) { var conversation = (NotificationData) data; Console.WriteLine($"Conversation: {conversation.EventBody.Id}"); } }; Console.WriteLine("Websocket connected, awaiting messages..."); Console.WriteLine("Press any key to remove conversations subscription."); Console.ReadKey(true); handler.RemoveSubscription($"v2.users.{_me.Id}.conversations"); Console.WriteLine("Conversations subscription removed, awaiting messages..."); Console.ReadKey(true); ``` ### Notification Topics and Classes for Deserialization The SDK contains a static class, [NotificationTopics](https://github.com/MyPureCloud/platform-client-sdk-dotnet/tree/master/build/src/PureCloudPlatform.Client.V2/Client/NotificationTopics.cs), that contains a dictionary of all of the known topics and the types that should be used for deserialization. This class exists to allow an application to dynamically see the defined topics and programatically specify the defined type for a known topic. This class also serves as a reference to the developer to know which classes go with which topics. _Note that the deserializer does not use this mapping; it uses the type provided to it when adding a topic subscription._ ## SDK Information ### REST Requests The SDK library uses [RestSharp](http://restsharp.org/) by default to make the REST requests. The majority of this work is done in [DefaultHttpClient.cs](https://github.com/MyPureCloud/platform-client-sdk-dotnet/blob/master/build/src/PureCloudPlatform.Client.V2/Client/ApiClient.cs) ### Inject a custom Http Client By default the SDK will use RestSharp's RestClient as the default http client. If you want to inject a new third party/custom implementation of client , you set the httpclient instance The CustomHttpClient should be an instance of AbstractHttpClient defined in the SDK. Which will implement the 'Execute' and 'ExecuteAsync' methods. Please find an example here. ```csharp public class CustomHttpClient : AbstractHttpClient { private HttpClient httpClient; public CustomHttpClient() : base() { httpClient = new HttpClient(); } public override IHttpRequest Execute(IHttpRequest request) { return httpClient.SendAsync(request).GetAwaiter().GetResult(); } public override async Task ExecuteAync(IHttpRequest request, CancellationToken cancellationToken = default(CancellationToken)) { return await httpClient.SendAsync(request, cancellationToken); } } Configuration.Default.ApiClient.HttpClient = new CustomHttpClient(); ``` ### Using MTLS authentication via a Gateway If there is MTLS authentication that needs to be set for a gateway server (i.e. if the Genesys Cloud requests must be sent through an intermediate API gateway or equivalent, with MTLS enabled), you can use SetMTLSCertificates to set the clients certificate. An example using `SetMTLSCertificates` to setup MTLS for gateway is shown below ```csharp ApiClient.GatewayConfiguration gatewayConfiguration = new ApiClient.GatewayConfiguration(); gatewayConfiguration.Host = "mygateway.mydomain.myextension"; gatewayConfiguration.Protocol = "https"; gatewayConfiguration.Port = 1443; gatewayConfiguration.PathParamsLogin = "myadditionalpathforlogin"; gatewayConfiguration.PathParamsApi = "myadditionalpathforapi"; Configuration.Default.ApiClient.GatewayConfig = gatewayConfiguration; var certPath = "path/to/cert.pfx"; var certPass = "x509Password"; Configuration.Default.ApiClient.SetMTLSCertificates(certPath, certPass); ``` ### Using Pre Commit and Post Commit Hooks For any custom requirements like pre validations or post cleanups (for ex: OCSP and CRL validation), we can inject the prehook and posthook functions. The SDK's default client will make sure the injected hook functions are executed. The Pre/Post Hook functions must have the following method signature shown below ```csharp IHttpRequest preHook(IHttpRequest request) IHttpResponse postHook(IHttpRepsonse response) ``` Here is an example of using a Pre Hook function: ```csharp private IHttpRequest preHook(IHttpRequest request) { try { Console.WriteLine("Running PreHook: Certificate Validation Checks"); // custom validation here Console.WriteLine("Certificate Validation Complete"); } catch (Exception ex) { Console.WriteLine($"Error in prehook validation: {ex.Message}"); throw ex; // Reject request if validation fails } } Configuration.Default.ApiClient.HttpClient.SetPreRequestHook(preHook); ``` ### Building from Source If you're working inside Visual Studio, adding the files to your project allows you to edit and build inside an IDE. 1. Clone the repo 2. Open the solution file: [PureCloudPlatform.Client.V2.sln](https://github.com/MyPureCloud/platform-client-sdk-dotnet/blob/master/build/PureCloudPlatform.Client.V2.sln) 3. Resolve/restore dependencies (`Update-Package –reinstall` in the _Package Manager Console_) 4. Build the project in Visual Studio Alternatively, the code can be compiled via the command line. The official builds do this using cross-platform tools: [compile.sh](https://github.com/MyPureCloud/platform-client-sdk-common/blob/master/resources/sdk/pureclouddotnet/scripts/compile.sh) ## SDK Source Code Generation The SDK is automatically regenerated and published from the API's definition after each API release. For more information on the build process, see the [platform-client-sdk-common](https://github.com/MyPureCloud/platform-client-sdk-common) project. ## Versioning The SDK's version is incremented according to the [Semantic Versioning Specification](https://semver.org/). The decision to increment version numbers is determined by [diffing the Platform API's swagger](https://github.com/purecloudlabs/platform-client-sdk-common/blob/master/modules/swaggerDiff.js) for automated builds, and optionally forcing a version bump when a build is triggered manually (e.g. releasing a bugfix). ## Support This package is intended to be forwards compatible with v2 of Genesys Cloud's Platform API. While the general policy for the API is not to introduce breaking changes, there are certain additions and changes to the API that cause breaking changes for the SDK, often due to the way the API is expressed in its swagger definition. Because of this, the SDK can have a major version bump while the API remains at major version 2. While the SDK is intended to be forward compatible, patches will only be released to the latest version. For these reasons, it is strongly recommended that all applications using this SDK are kept up to date and use the latest version of the SDK. For any issues, questions, or suggestions for the SDK, visit the [Genesys Cloud Developer Community](https://community.genesys.com/communities/community-home1/digestviewer?CommunityKey=a39cc4d6-857e-43cb-be7b-019581ab9f38).