{"id":22281794,"url":"https://github.com/recurly/recurly-client-dotnet","last_synced_at":"2026-02-06T22:04:31.803Z","repository":{"id":37057935,"uuid":"484994","full_name":"recurly/recurly-client-dotnet","owner":"recurly","description":"A .NET API wrapper for Recurly.","archived":false,"fork":false,"pushed_at":"2025-12-17T19:39:43.000Z","size":18815,"stargazers_count":88,"open_issues_count":21,"forks_count":81,"subscribers_count":73,"default_branch":"v3-v2021-02-25","last_synced_at":"2026-01-13T11:43:41.595Z","etag":null,"topics":["csharp","dotnet","recurly"],"latest_commit_sha":null,"homepage":"https://developers.recurly.com","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/recurly.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2010-01-23T10:58:07.000Z","updated_at":"2025-12-12T21:02:17.000Z","dependencies_parsed_at":"2025-12-08T20:06:27.072Z","dependency_job_id":null,"html_url":"https://github.com/recurly/recurly-client-dotnet","commit_stats":null,"previous_names":["recurly/recurly-client-net"],"tags_count":268,"template":false,"template_full_name":null,"purl":"pkg:github/recurly/recurly-client-dotnet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recurly%2Frecurly-client-dotnet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recurly%2Frecurly-client-dotnet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recurly%2Frecurly-client-dotnet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recurly%2Frecurly-client-dotnet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/recurly","download_url":"https://codeload.github.com/recurly/recurly-client-dotnet/tar.gz/refs/heads/v3-v2021-02-25","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/recurly%2Frecurly-client-dotnet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28472626,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T22:13:38.078Z","status":"ssl_error","status_checked_at":"2026-01-15T22:12:11.737Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet","recurly"],"created_at":"2024-12-03T16:22:11.282Z","updated_at":"2026-01-15T22:21:35.737Z","avatar_url":"https://github.com/recurly.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Recurly\n\n[![Nuget](https://img.shields.io/static/v1?label=nuget\u0026message=recurly\u0026color=purple)](https://www.nuget.org/packages/Recurly/)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)\n\nThis repository houses the official dotnet client for Recurly's V3 API.\n\n\u003e *Note*:\n\u003e If you were looking for the V2 client, see the [v2 branch](https://github.com/recurly/recurly-client-net/tree/v2).\n\nDocumentation for the HTTP API and example code can be found\n[on our Developer Portal](https://developers.recurly.com/api/v2019-10-10/).\n\n## Getting Started\n\n### Installing\n\nThis package is published on Nuget under the name [Recurly](https://www.nuget.org/packages/Recurly).\nWe recommend using Nuget to install and maintain this dependency:\n\n```\ndotnet add package Recurly --version 5.*\n```\n\nIf you are specifying in your `.csproj` file:\n\n```xml\n\u003cItemGroup\u003e\n  \u003cPackageReference Include=\"Recurly\" Version=\"5.*\" /\u003e\n  \u003c!-- ... --\u003e\n\u003c/ItemGroup\u003e\n```\n\n\u003e *Note*: We try to follow [semantic versioning](https://semver.org/) and will only apply breaking changes to major versions.\n\n### Creating a client\n\nClient instances are now explicitly created and referenced as opposed to V2's use of global, statically\ninitialized clients.\n\nThis makes multithreaded environments simpler and provides one location where every\noperation can be found (rather than having them spread out among classes).\n\n`new Recurly.Client(apiKey)` initializes a new client. It only requires an API key\nwhich can be obtained on the [API Credentials Page](https://app.recurly.com/go/integrations/api_keys).\n\n```csharp\n// Add this on the top of your file\nusing Recurly;\nusing Recurly.Resources;\n\nconst apiKey = \"83749879bbde395b5fe0cc1a5abf8e5\";\nvar client = new Recurly.Client(apiKey);\nvar sub = client.GetSubscription(\"uuid-abcd123456\")\n```\n\nTo access Recurly API in Europe, you will need to specify the EU Region in the `ClientOptions`:\n```csharp\n// Add this on the top of your file\nusing Recurly;\nusing Recurly.Resources;\n\nconst apiKey = \"83749879bbde395b5fe0cc1a5abf8e5\";\nvar options = new ClientOptions()\n{\n    Region = ClientOptions.Regions.EU\n};\nvar client = new Recurly.Client(apiKey, options);\nvar sub = client.GetSubscription(\"uuid-abcd123456\")\n```\n\nOptional arguments can be provided through object initializers.\n\n```csharp\nvar client = new Recurly.Client(apiKey) { Timeout = 5000 }\n```\n\n### Operations\n\nThe `Recurly.Client` contains every `operation` you can perform on the site as a list of methods. Each method is documented explaining\nthe types and descriptions for each input and return type.\n\n### Async Operations\n\nEach operation in the `Recurly.Client` has an async equivalent method which ends in `Async`. Async operations return `Task\u003cResource\u003e`\nwhich can be `await`ed:\n\n```csharp\nvar client = new Recurly.Client(apiKey);\nvar sub = await client.GetSubscription(\"uuid-abcd123456\");\n```\n\nAsync operations also support cancellation tokens. Here is an example of canceling a request before it executes:\n\n```csharp\nvar cancellationTokenSource = new CancellationTokenSource();\nvar task = await client.GetSubscription(\"uuid-abcd123456\", cancellationTokenSource.Token);\n\n// Cancel the request before it finishes which will throw a\n// System.Threading.Tasks.TaskCanceledException\ncancellationTokenSource.Cancel();\n\ntask.Wait();\nvar sub = task.Result;\nConsole.WriteLine($\"Subscription: {sub.Uuid}\");\n```\n\n**Warning**: Be careful cancelling requests as you have no way of knowing whether or not they were completed\nby the server. We only guarantee that server state does not change on GET requests.\n\n### Pagination\n\nPagination is done by the class `Recurly.Pager\u003cT\u003e`. All `List*` methods on the client return an instance of this class.\nThe pager supports the `IEnumerable` and `IEnumerator` interfaces. The easiest way to use the pager is with `foreach`.\n\n```csharp\nvar accounts = client.GetAccounts();\nforeach(Account account in accounts)\n{\n  Console.WriteLine(account.Code);\n}\n```\n\nThe `FetchNextPage` method provides more control over the network calls. We recommend using this\ninterface for writing scripts that iterate over many pages. This allows you\nto catch exceptions and safely retry without double processing or missing some elements:\n\n```csharp\nvar accounts = client.ListAccounts();\nwhile(accounts.HasMore)\n{\n    Console.WriteLine(\"Fetching next page...\");\n    accounts.FetchNextPage();\n    foreach(Account a in accounts.Data)\n    {\n      Console.WriteLine($\"Account: {a.CreatedAt}\");\n    }\n}\n```\n\nFor async pagination, await on `FetchNextPageAsync`:\n\n```csharp\nvar accounts = client.ListAccounts();\nwhile(accounts.HasMore)\n{\n    Console.WriteLine(\"Fetching next page...\");\n    await accounts.FetchNextPageAsync();\n    foreach(Account a in accounts.Data)\n    {\n      Console.WriteLine($\"Account: {a.CreatedAt}\");\n    }\n}\n```\n\n#### Query Params\n\nQuery params can be passed to `List*` methods as named arguments. These will be used\nto sort and filter the resources.\n\n```csharp\nvar accounts = client.ListAccounts(\n    limit: 200,\n    beginTime: new DateTime(2019, 1, 1)\n);\n```\n\n#### Additional Pager Methods\n\nIn addition to the methods to facilitate pagination, the Pager class provides 2 helper methods:\n\n1. First\n2. Count\n\n##### First\n\nThe Pager's `First` method can be used to fetch only the first resource from the endpoint for the given parameters.\n\n```csharp\nvar beginTime = new DateTime(2020, 1, 1);\nvar accounts = client.ListAccounts(\n    beginTime: beginTime\n);\nvar account = accounts.First();\nConsole.WriteLine(account.Code);\n```\n\n##### Count\n\nThe Pager's `Count` method will return the total number of resources that are available at the requested endpoint for the given parameters.\n\n```csharp\nvar beginTime = new DateTime(2020, 1, 1);\nvar accounts = client.ListAccounts(\n    beginTime: beginTime\n);\nvar total = accounts.Count();\nConsole.WriteLine($\"There are {total} accounts created since {beginTime}\");\n```\n\n### Creating Resources\n\nEvery `Create*` or `Update*` method on the client takes a specific Request type to form the request.\nThis allows you to create requests in a type-safe manner. Request types are not necessarily 1-to-1 mappings of response types.\n\n```csharp\nvar accountReq = new AccountCreate()\n{\n  Code = \"myaccountcode\",\n  Address = new Address() {\n    FirstName = \"Benjamin\",\n    LastName = \"DuMonde\",\n    Street1 = \"123 Canal St.\",\n    PostalCode = \"70115\",\n    Region = \"LA\",\n    City = \"New Orleans\",\n    Country = \"US\"\n  }\n};\n\n// CreateAccount takes an AccountCreate object and returns an Account object\nAccount account = client.CreateAccount(accountReq);\nConsole.WriteLine(account.Address.City); // \"New Orleans\"\n```\n\n### Error Handling\n\nThis library currently throws 2 types of exceptions. They both exist as subclasses of `Recurly.RecurlyError`.\n\n1. `Recurly.Errors.ApiError`\n2. `Recurly.Errors.NetworkError`\n\n`ApiError`s come from the Recurly API and each endpoint in the documentation describes the types of errors it\nmay return. These errors generally mean that something was wrong with the request. There are a number of subclasses\nto `ApiError` which are derived from the error responses `type` json key.  A common scenario might be a `Validation` error:\n\n```csharp\ntry\n{\n  var accountReq = new AccountCreate()\n  {\n    Code = \"myaccountcode\",\n  };\n\n  Account acct = client.CreateAccount(accountReq);\n}\ncatch (Recurly.Errors.Validation ex)\n{\n  // Here we have a validation error and might want to\n  // pass this information back to the user to fix\n  Console.WriteLine($\"Validation Error: {ex.Error.Message}\");\n}\ncatch (Recurly.Errors.ApiError ex)\n{\n  // Use base class ApiError to catch a generic error from the API\n  Console.WriteLine($\"Unexpected Recurly Error: {ex.Error.Message}\");\n}\n```\n\n`Recurly.Errors.NetworkError`s don't come from Recurly's servers, but instead are triggered by some problem\nrelated to the network. Depending on the context, you can often automatically retry these calls.\nGETs are always safe to retry but be careful about automatically re-trying any other call that might mutate state on the server side\nas we cannot guarantee that it will not be executed twice.\n\n```csharp\ntry\n{\n  Account acct = client.GetAccount(\"code-my-account-code\");\n}\ncatch (Recurly.Errors.NetworkError ex)\n{\n  // Here you might want to determine what kind of NetworkError this is\n  // The options for ExceptionStatus are defined here: https://docs.microsoft.com/en-us/dotnet/api/system.net.webexceptionstatus\n  switch (ex.ExceptionStatus)\n  {\n    case WebException.Timeout:\n      // The server timed out\n      // probably safe to retry after waiting a moment\n      break;\n    case WebException.ConnectFailure:\n      // Could not connect to Recurly's servers\n      // This is hopefully a temporary problem and is safe to retry after waiting a moment\n      break;\n    default:\n      // If we don't know what to do with it, we should\n      // probably re-raise and let our web framework or logger handle it\n      throw;\n  }\n}\n```\n\n### HTTP Metadata\n\nSometimes you might want to get some additional information about the underlying HTTP request and response. Instead of returning this information directly and forcing the programmer to unwrap it, we inject this metadata into the top level resource that was returned. You can access the response by calling `GetResponse()` on any Resource.\n\n```csharp\nAccount account = client.GetAccount(accountId);\nResponse response = account.GetResponse();\nresponse.RawResponse // String body of the API response\nresponse.StatusCode // HTTP status code of the API response\nresponse.RequestId // \"5b7019241a21d314-ATL\"\nresponse.Headers // IList\u003cParameter\u003e of all API response headers\n```\n\nRate Limit information is also accessible on the `Response` class. These values will be `null` when the corresponding headers are absent from the response. More information can be found on the developer portal's [Rate Limits](https://developers.recurly.com/api/v2019-10-10/index.html#section/Getting-Started/Limits) section.\n```csharp\nresponse.RateLimit // 2000  \nresponse.RateLimitRemaining // 1990\nresponse.RateLimitReset // 1595965380\n```\n\n### A Note on Headers\n\nIn accordance with [section 4.2 of RFC 2616](https://www.ietf.org/rfc/rfc2616.txt), HTTP header fields are case-insensitive.\n\n### Webhooks\n\nRecurly can send webhooks to any publicly accessible server.\nWhen an event in Recurly triggers a webhook (e.g., an account is opened),\nRecurly will attempt to send this notification to the endpoint(s) you specify.\nYou can specify up to 10 endpoints through the application. All notifications will\nbe sent to all configured endpoints for your site. \n\nSee our [product docs](https://docs.recurly.com/docs/webhooks) to learn more about webhooks\nand see our [dev docs](https://developers.recurly.com/pages/webhooks.html) to learn about what payloads\nare available.\n\nAlthough our API is now JSON, our webhooks are currently still in XML format. This library is not responsible for webhooks, but the quickest way to handle them now is by using the [XmlDocument class](https://docs.microsoft.com/en-us/dotnet/api/system.xml.xmldocument). This class has helpful methods for\nparsing XML and using XPath to inspect the elements. You could also look into mapping them to custom types if you want a more friendly experience. We will be supporting this in the near future.\n\n```csharp\n// XmlDocument is in System.Xml\n// using System.Xml;\n\n// This XML will arrive at the endpoint you have specified in Recurly.\n// We're putting it in a string literal here for demonstration purposes\nvar xml = \"\u003c?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?\u003e\"\n+ \"\u003cnew_account_notification\u003e\"\n+ \"\u003caccount\u003e\"\n+   \"\u003caccount_code\u003eabc\u003c/account_code\u003e\"\n+   \"\u003cusername nil=\\\"true\\\"\u003e\u003c/username\u003e\"\n+   \"\u003cemail\u003everena@example.com\u003c/email\u003e\"\n+   \"\u003cfirst_name\u003eVerena\u003c/first_name\u003e\"\n+   \"\u003clast_name\u003eExample\u003c/last_name\u003e\"\n+   \"\u003ccompany_name nil=\\\"true\\\"\u003e\u003c/company_name\u003e\"\n+ \"\u003c/account\u003e\"\n+ \"\u003c/new_account_notification\u003e\";\n\nvar doc = new XmlDocument();\ndoc.LoadXml(xml);\n\n// This element will always contain the event name\n// see the documentation for which events are supported\nvar eventName = doc.DocumentElement.Name;\n\n// delegate to the code responsible for each event\n// make sure you have a default fallback case as we may add events\n// at any time.\nswitch (eventName) {\n    case \"new_account_notification\":\n        // handle new account notifcation\n        var code = doc.DocumentElement.SelectSingleNode(\"//account/account_code\")\n        Console.WriteLine($\"New Account Created in Recurly: {code.InnerText}\");\n        // prints \"abc\"\n        break;\n    default:\n        Console.WriteLine($\"Ignoring webhook with event name: {eventName}\");\n        break;\n}\n```\n\n\n### Contributing\n\nPlease see our [Contributing Guide](CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frecurly%2Frecurly-client-dotnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frecurly%2Frecurly-client-dotnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frecurly%2Frecurly-client-dotnet/lists"}