Configuration
Every option of the client, what it reads from the environment, and what it refuses.
Options
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}");| Option | What it does |
|---|---|
| ApiKey | The workspace API key, beginning oe_live_ or oe_test_. |
| AccessToken | An OAuth access token, for an app a person connected. Pass a key or a token, not both. |
| AccessTokenProvider | A function that returns the access token. It runs before every request, so it is the place to renew a token. |
| BaseUrl | The API origin. A bare host such as localhost:2222 gets its scheme added. |
| HttpClient | The HttpClient your application already has, such as one from IHttpClientFactory or one with a test handler. The client never disposes it. |
| Timeout | How long one attempt may take. The default is 30 seconds, TimeSpan.Zero turns it off, and uploads wait at least ten minutes. |
| MaxRetries | How many times a failed request is tried again. The default is 2, and 0 sends once. |
| UserAgent | Replaces the User-Agent header, which is openemail-dotnet/ and the version. |
| Headers | Headers 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.
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.
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.
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.
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_oroe_test_, and a key together with an access token. - Sending a credential over plain
http:, unless the server is on this machine atlocalhost, a127.x.x.xaddress or::1. - A base URL on
0.0.0.0, which is an address a server listens on. Use127.0.0.1with 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.
var label = await client.Raw.RequestAsync("/labels", "POST", body: new Body { ["name"] = "Invoices" }); Console.WriteLine(label?["id"]);