Skip to the documentation
C#

Configuration

Every option of the client, what it reads from the environment, and what it refuses.

Options

client.cs
using var http = new HttpClient(); var client = new OpenEmailClient(new OpenEmailOptions{    ApiKey = Environment.GetEnvironmentVariable("OPENEMAIL_API_KEY"),    BaseUrl = "https://api.openemail.uk",    Timeout = TimeSpan.FromSeconds(30),    MaxRetries = 2,    HttpClient = http,    Headers = { ["X-Team"] = "billing" },}); Console.WriteLine($"{client.Mode} {client.Raw.BaseUrl}");
OptionWhat it does
ApiKeyThe workspace API key, beginning oe_live_ or oe_test_.
AccessTokenAn OAuth access token, for an app a person connected. Pass a key or a token, not both.
AccessTokenProviderA function that returns the access token. It runs before every request, so it is the place to renew a token.
BaseUrlThe API origin. A bare host such as localhost:2222 gets its scheme added.
HttpClientThe HttpClient your application already has, such as one from IHttpClientFactory or one with a test handler. The client never disposes it.
TimeoutHow long one attempt may take. The default is 30 seconds, TimeSpan.Zero turns it off, and uploads wait at least ten minutes.
MaxRetriesHow many times a failed request is tried again. The default is 2, and 0 sends once.
UserAgentReplaces the User-Agent header, which is openemail-dotnet/ and the version.
HeadersHeaders added to every request.

Anything you leave out is read from the environment: OPENEMAIL_API_KEY, then OPENEMAIL_ACCESS_TOKEN, and OPENEMAIL_BASE_URL. client.Mode says whether the key is a live or a test key.

Cancellation and timeouts

Every method takes a CancellationToken last. Cancelling it stops the call at once, retries included, and throws OperationCanceledException, while Timeout bounds each attempt.

deadline.cs
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(10)); var thread = await client.Threads.GetAsync("CAHk7pQ2x9LmZ4-mail.example.com", cancellationToken: deadline.Token); Console.WriteLine(thread["messageCount"]);

Options on a call

Query parameters and call settings are named arguments under the names of the API, such as limit:, cursor: and idempotencyKey:. Every method takes apiKey: to act with another key for that call only, so one process can serve several workspaces with one client.

another-workspace.cs
var page = await client.Emails.ListAsync(    status: new[] { "failed", "bounced" },    limit: 50,    apiKey: Environment.GetEnvironmentVariable("OTHER_WORKSPACE_KEY")); Console.WriteLine($"{page.Count} {page.HasMore}");

OAuth access tokens

An app a person connected with OAuth holds an access token instead of an API key. Pass it as AccessToken, or pass AccessTokenProvider and renew the token there when it is close to expiring, so the client never has to be rebuilt.

access-token.cs
var current = "token-from-your-oauth-flow"; var client = new OpenEmailClient(new OpenEmailOptions{    AccessTokenProvider = _ => Task.FromResult(current),}); var me = await client.Me.GetAsync(); Console.WriteLine(me["workspaceId"]);

ASP.NET Core

Register one client for the whole application. It holds no connection of its own to dispose, so a singleton is the right lifetime.

Program.cs
using OpenEmail; var builder = WebApplication.CreateBuilder(args); builder.Services.AddSingleton(new OpenEmailClient(new OpenEmailOptions{    ApiKey = builder.Configuration["OpenEmail:ApiKey"],})); var app = builder.Build(); app.MapPost("/invoices/{number}/send", async (string number, OpenEmailClient client, CancellationToken cancellationToken) =>{    var sent = await client.Emails.SendAsync(new Body    {        ["from"] = "[email protected]",        ["to"] = "[email protected]",        ["subject"] = $"Invoice {number}",        ["text"] = "The invoice is attached.",    }, idempotencyKey: $"invoice-{number}", cancellationToken: cancellationToken);     return Results.Ok(new { id = (string?)sent["id"] });}); app.Run();

What the client refuses

  • A key that does not begin oe_live_ or oe_test_, and a key together with an access token.
  • Sending a credential over plain http:, unless the server is on this machine at localhost, a 127.x.x.x address or ::1.
  • A base URL on 0.0.0.0, which is an address a server listens on. Use 127.0.0.1 with the same port.
  • An empty id, or one made only of dots, which a URL parser would remove.
  • Following a redirect. The HTTP client the package builds never follows one, so a credential never travels to another host.

Printing a client or an exception never shows a credential. ToString() on a client gives its mode and its base URL, and nothing else.

An endpoint without a method

client.Raw.RequestAsync() calls any path with the credential, base URL, timeout and retry policy of the client. The path must begin with a single /, and a path that leaves the origin of the base URL is refused.

request.cs
var label = await client.Raw.RequestAsync("/labels", "POST", body: new Body { ["name"] = "Invoices" }); Console.WriteLine(label?["id"]);