---
title: "client.Billing"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://openemail.uk/docs/csharp/reference/billing"
area: "C#"
category: "Reference"
---

# client.Billing

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

The plan of the workspace and what it costs, as Plans & Billing shows them: the plan and its usage, the plans on sale, pay as you go, every charge with its invoice, and the links that start a checkout or open the billing portal. Only the workspace owner reaches it, and nothing is paid through the API.

### `Billing.GetAsync`

Read the plan

```csharp
Task<JsonObject> GetAsync(string? apiKey = null, CancellationToken cancellationToken = default)
```

Returns the plan of the workspace and what it includes, as Plans & Billing shows it: how it is billed, when it renews or ends and the next charge, how many domains it holds, how much of this month's sends and today's AI actions are used, and pay as you go. `metered` is false when billing is off on the install, and then nothing is capped.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `plan`, `planName`, `interval`, `renewsAt`, `nextCharge`, `domains`, `usage` and `payAsYouGo`.

**Example**

```csharp
var billing = await client.Billing.GetAsync();
var sends = billing["usage"]?["sends"];

Console.WriteLine($"{billing["planName"]}: {sends?["used"]} of {sends?["limit"]?.ToString() ?? "unlimited"} sends this month");
```

**Notes**

- A limit of `null` means the plan has none.

Also available in: API [`GET /billing`](https://openemail.uk/docs/api/reference/billing#get-billing); TypeScript [`billing.get()`](https://openemail.uk/docs/sdk/reference/billing#get); Python [`billing.get()`](https://openemail.uk/docs/python/reference/billing#get); Ruby [`billing.get`](https://openemail.uk/docs/ruby/reference/billing#get); PHP [`billing->get`](https://openemail.uk/docs/php/reference/billing#get); Go [`Billing.Get`](https://openemail.uk/docs/go/reference/billing#get); Java [`billing().get`](https://openemail.uk/docs/java/reference/billing#get); CLI [`openemail billing get`](https://openemail.uk/docs/cli/reference/billing#billing-get).

### `Billing.ListPlansAsync`

List the plans

```csharp
Task<JsonObject> ListPlansAsync(
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns every plan, cheapest first, with its price in US cents and what it includes. `available` says whether it is sold on the install, and `intervals` the billing intervals it is sold at.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `data`, a list with one object per plan.

**Example**

```csharp
var plans = await client.Billing.ListPlansAsync();

Console.WriteLine(plans.ToJsonString());
```

Also available in: API [`GET /billing/plans`](https://openemail.uk/docs/api/reference/billing#get-billing-plans); TypeScript [`billing.listPlans()`](https://openemail.uk/docs/sdk/reference/billing#listPlans); Python [`billing.list_plans()`](https://openemail.uk/docs/python/reference/billing#listPlans); Ruby [`billing.list_plans`](https://openemail.uk/docs/ruby/reference/billing#listPlans); PHP [`billing->listPlans`](https://openemail.uk/docs/php/reference/billing#listPlans); Go [`Billing.ListPlans`](https://openemail.uk/docs/go/reference/billing#listPlans); Java [`billing().listPlans`](https://openemail.uk/docs/java/reference/billing#listPlans); CLI [`openemail billing list-plans`](https://openemail.uk/docs/cli/reference/billing#billing-list-plans).

### `Billing.GetUsageAsync`

Read the usage history

```csharp
Task<JsonObject> GetUsageAsync(
    string? days = null,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns the sends and AI actions of each day, and what pay as you go extras cost each month, as the Usage charts show them.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `days` (`string?`): How many days back to go, from 1 to 3,660, or `all` (`BillingUsageWindows.All`) for every day since the workspace was made. The server defaults to 30.
- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `days`, `months`, `dailyAiActions` and `monthlySends`.

**Example**

```csharp
var usage = await client.Billing.GetUsageAsync(days: "90");

Console.WriteLine($"{(usage["days"]?.AsArray() ?? []).Sum(row => (long?)row?["sends"] ?? 0)} sends in the last 90 days");

foreach (var month in usage["months"]?.AsArray() ?? [])
{
    Console.WriteLine($"{month?["month"]}: {month?["cents"]} cents of extras");
}
```

Also available in: API [`GET /billing/usage`](https://openemail.uk/docs/api/reference/billing#get-billing-usage); TypeScript [`billing.getUsage()`](https://openemail.uk/docs/sdk/reference/billing#getUsage); Python [`billing.get_usage()`](https://openemail.uk/docs/python/reference/billing#getUsage); Ruby [`billing.get_usage`](https://openemail.uk/docs/ruby/reference/billing#getUsage); PHP [`billing->getUsage`](https://openemail.uk/docs/php/reference/billing#getUsage); Go [`Billing.GetUsage`](https://openemail.uk/docs/go/reference/billing#getUsage); Java [`billing().getUsage`](https://openemail.uk/docs/java/reference/billing#getUsage); CLI [`openemail billing get-usage`](https://openemail.uk/docs/cli/reference/billing#billing-get-usage).

### `Billing.ListAlertsAsync`

List the billing alerts

```csharp
Task<JsonObject> ListAlertsAsync(
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns what the billing banner of the app would say now, most urgent first: a failed payment, paused pay as you go, sending or AI paused at a limit, a plan about to end, an allowance nearly used and a charge coming soon. An empty list means there is nothing to say.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `data`, a list with one object per alert.

**Example**

```csharp
using OpenEmail.Constants;

var alerts = await client.Billing.ListAlertsAsync();

foreach (var alert in alerts["data"]?.AsArray() ?? [])
{
    if ((string?)alert?["severity"] == BillingAlertSeverities.Blocked)
    {
        Console.WriteLine($"Blocked: {alert?["kind"]}");
    }
}
```

**Notes**

- An alert keeps its `id` while the same thing is true, so one that was dismissed can stay dismissed.

Also available in: API [`GET /billing/alerts`](https://openemail.uk/docs/api/reference/billing#get-billing-alerts); TypeScript [`billing.listAlerts()`](https://openemail.uk/docs/sdk/reference/billing#listAlerts); Python [`billing.list_alerts()`](https://openemail.uk/docs/python/reference/billing#listAlerts); Ruby [`billing.list_alerts`](https://openemail.uk/docs/ruby/reference/billing#listAlerts); PHP [`billing->listAlerts`](https://openemail.uk/docs/php/reference/billing#listAlerts); Go [`Billing.ListAlerts`](https://openemail.uk/docs/go/reference/billing#listAlerts); Java [`billing().listAlerts`](https://openemail.uk/docs/java/reference/billing#listAlerts); CLI [`openemail billing list-alerts`](https://openemail.uk/docs/cli/reference/billing#billing-list-alerts).

### `Billing.ListCountriesAsync`

List the billing countries

```csharp
Task<JsonObject> ListCountriesAsync(
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns every country a billing address can be in, as the two-letter ISO 3166 codes `SaveInvoiceDetailsAsync` takes as `country`.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with the codes in `data`, a list of strings.

**Example**

```csharp
var countries = await client.Billing.ListCountriesAsync();

Console.WriteLine(countries.ToJsonString());
```

Also available in: API [`GET /billing/countries`](https://openemail.uk/docs/api/reference/billing#get-billing-countries); TypeScript [`billing.listCountries()`](https://openemail.uk/docs/sdk/reference/billing#listCountries); Python [`billing.list_countries()`](https://openemail.uk/docs/python/reference/billing#listCountries); Ruby [`billing.list_countries`](https://openemail.uk/docs/ruby/reference/billing#listCountries); PHP [`billing->listCountries`](https://openemail.uk/docs/php/reference/billing#listCountries); Go [`Billing.ListCountries`](https://openemail.uk/docs/go/reference/billing#listCountries); Java [`billing().listCountries`](https://openemail.uk/docs/java/reference/billing#listCountries); CLI [`openemail billing list-countries`](https://openemail.uk/docs/cli/reference/billing#billing-list-countries).

### `Billing.GetPayAsYouGoAsync`

Read pay as you go

```csharp
Task<JsonObject> GetPayAsYouGoAsync(
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns pay as you go on the workspace: whether usage past the allowances is billed, the monthly limit and what extras cost so far, the current billing period once it is set up, the limits that can be chosen and the prices.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with the state, `limits`, `prices` and `period`.

**Example**

```csharp
var payAsYouGo = await client.Billing.GetPayAsYouGoAsync();

Console.WriteLine($"{((bool?)payAsYouGo["enabled"] == true ? "On" : "Off")}, {payAsYouGo["spentCents"]} of {payAsYouGo["limitCents"]} cents spent");
Console.WriteLine($"Limits on offer: {string.Join(", ", payAsYouGo["limits"]?.AsArray() ?? [])}");
```

**Notes**

- Amounts are in US cents.

Also available in: API [`GET /billing/pay-as-you-go`](https://openemail.uk/docs/api/reference/billing#get-billing-pay-as-you-go); TypeScript [`billing.getPayAsYouGo()`](https://openemail.uk/docs/sdk/reference/billing#getPayAsYouGo); Python [`billing.get_pay_as_you_go()`](https://openemail.uk/docs/python/reference/billing#getPayAsYouGo); Ruby [`billing.get_pay_as_you_go`](https://openemail.uk/docs/ruby/reference/billing#getPayAsYouGo); PHP [`billing->getPayAsYouGo`](https://openemail.uk/docs/php/reference/billing#getPayAsYouGo); Go [`Billing.GetPayAsYouGo`](https://openemail.uk/docs/go/reference/billing#getPayAsYouGo); Java [`billing().getPayAsYouGo`](https://openemail.uk/docs/java/reference/billing#getPayAsYouGo); CLI [`openemail billing get-pay-as-you-go`](https://openemail.uk/docs/cli/reference/billing#billing-get-pay-as-you-go).

### `Billing.SetPayAsYouGoAsync`

Turn pay as you go on or off

```csharp
Task<JsonObject> SetPayAsYouGoAsync(
    IReadOnlyDictionary<string, object?> body,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Turns pay as you go on or off, as the switch on Plans & Billing does. Off takes effect at once, and usage stops at the allowances. On takes effect at once when a card is already saved. The first time, `checkoutUrl` is a link where the person adds a card, and pay as you go starts once they have: call `ConfirmPayAsYouGoAsync` after they return.

Nothing is paid through the API: the person pays, changes the card or cancels on the billing provider's page the link opens, as they do from the app.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:write`.

**Parameters**

- `enabled` (`bool`, required): `true` to turn it on, `false` to turn it off.
- `theme` (`string`): `light` or `dark`, for the page the link opens, as in `OpenEmail\Constants\BillingThemes`.
- `locale` (`string`): A language tag such as `de` or `pt-PT`, for the page the link opens.
- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `enabled`, `suspended`, `activating` and `checkoutUrl`.

**Example**

```csharp
var result = await client.Billing.SetPayAsYouGoAsync(new Body { ["enabled"] = true, ["theme"] = "dark" });

if (result["checkoutUrl"] is not null)
{
    Console.WriteLine($"Add a card here: {result["checkoutUrl"]}");
}
```

**Notes**

- With an OAuth access token it asks for a verification code: until the app has verified one, it is refused with 403 `step_up_required`. An API key is never asked.
- Turning it on where pay as you go is not set up on the install is 503 `billing_not_configured`.
- The SDK does not retry it.

Also available in: API [`PATCH /billing/pay-as-you-go`](https://openemail.uk/docs/api/reference/billing#patch-billing-pay-as-you-go); TypeScript [`billing.setPayAsYouGo()`](https://openemail.uk/docs/sdk/reference/billing#setPayAsYouGo); Python [`billing.set_pay_as_you_go()`](https://openemail.uk/docs/python/reference/billing#setPayAsYouGo); Ruby [`billing.set_pay_as_you_go`](https://openemail.uk/docs/ruby/reference/billing#setPayAsYouGo); PHP [`billing->setPayAsYouGo`](https://openemail.uk/docs/php/reference/billing#setPayAsYouGo); Go [`Billing.SetPayAsYouGo`](https://openemail.uk/docs/go/reference/billing#setPayAsYouGo); Java [`billing().setPayAsYouGo`](https://openemail.uk/docs/java/reference/billing#setPayAsYouGo); CLI [`openemail billing set-pay-as-you-go`](https://openemail.uk/docs/cli/reference/billing#billing-set-pay-as-you-go).

### `Billing.SetPayAsYouGoLimitAsync`

Set the pay as you go limit

```csharp
Task<JsonObject> SetPayAsYouGoLimitAsync(
    IReadOnlyDictionary<string, object?> body,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Sets the monthly spending limit of pay as you go, in US cents, one of the amounts `GetPayAsYouGoAsync` lists in `limits`. Extras stop for the rest of a calendar month once it is reached.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:write`.

**Parameters**

- `limitCents` (`int`, required): The limit in US cents: 1,000, 2,500, 5,000, 10,000, 25,000, 50,000, 100,000, 250,000 or 500,000, the amounts in `OpenEmail.Constants.PayAsYouGoLimitsCents.All`.
- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `limitCents`.

**Example**

```csharp
var saved = await client.Billing.SetPayAsYouGoLimitAsync(new Body { ["limitCents"] = 10000 });

Console.WriteLine(saved.ToJsonString());
```

**Notes**

- With an OAuth access token it asks for a verification code: until the app has verified one, it is refused with 403 `step_up_required`. An API key is never asked.
- A workspace where pay as you go was never set up is 409 `pay_as_you_go_not_enrolled`.
- The SDK does not retry it.

Also available in: API [`PUT /billing/pay-as-you-go/limit`](https://openemail.uk/docs/api/reference/billing#put-billing-pay-as-you-go-limit); TypeScript [`billing.setPayAsYouGoLimit()`](https://openemail.uk/docs/sdk/reference/billing#setPayAsYouGoLimit); Python [`billing.set_pay_as_you_go_limit()`](https://openemail.uk/docs/python/reference/billing#setPayAsYouGoLimit); Ruby [`billing.set_pay_as_you_go_limit`](https://openemail.uk/docs/ruby/reference/billing#setPayAsYouGoLimit); PHP [`billing->setPayAsYouGoLimit`](https://openemail.uk/docs/php/reference/billing#setPayAsYouGoLimit); Go [`Billing.SetPayAsYouGoLimit`](https://openemail.uk/docs/go/reference/billing#setPayAsYouGoLimit); Java [`billing().setPayAsYouGoLimit`](https://openemail.uk/docs/java/reference/billing#setPayAsYouGoLimit); CLI [`openemail billing set-pay-as-you-go-limit`](https://openemail.uk/docs/cli/reference/billing#billing-set-pay-as-you-go-limit).

### `Billing.ConfirmPayAsYouGoAsync`

Confirm pay as you go

```csharp
Task<JsonObject> ConfirmPayAsYouGoAsync(
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Checks with the billing provider whether the card from a pay as you go checkout was saved, and records it, as the app does when the person comes back from the checkout. `enabled` says whether pay as you go is on now.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:write`.

**Parameters**

- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `enabled`.

**Example**

```csharp
var confirmation = await client.Billing.ConfirmPayAsYouGoAsync();

Console.WriteLine($"{((bool?)confirmation["enabled"] == true ? "Pay as you go is on" : "No card was saved")}");
```

**Notes**

- With an OAuth access token it asks for a verification code: until the app has verified one, it is refused with 403 `step_up_required`. An API key is never asked.
- It is safe to call again.
- The SDK does not retry it.

Also available in: API [`POST /billing/pay-as-you-go/confirm`](https://openemail.uk/docs/api/reference/billing#post-billing-pay-as-you-go-confirm); TypeScript [`billing.confirmPayAsYouGo()`](https://openemail.uk/docs/sdk/reference/billing#confirmPayAsYouGo); Python [`billing.confirm_pay_as_you_go()`](https://openemail.uk/docs/python/reference/billing#confirmPayAsYouGo); Ruby [`billing.confirm_pay_as_you_go`](https://openemail.uk/docs/ruby/reference/billing#confirmPayAsYouGo); PHP [`billing->confirmPayAsYouGo`](https://openemail.uk/docs/php/reference/billing#confirmPayAsYouGo); Go [`Billing.ConfirmPayAsYouGo`](https://openemail.uk/docs/go/reference/billing#confirmPayAsYouGo); Java [`billing().confirmPayAsYouGo`](https://openemail.uk/docs/java/reference/billing#confirmPayAsYouGo); CLI [`openemail billing confirm-pay-as-you-go`](https://openemail.uk/docs/cli/reference/billing#billing-confirm-pay-as-you-go).

### `Billing.StartCheckoutAsync`

Start a checkout

```csharp
Task<JsonObject> StartCheckoutAsync(
    IReadOnlyDictionary<string, object?> body,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Starts buying a paid plan for a workspace on Free, as choosing a plan in the app does, and returns the link to the checkout. Nothing is charged until the person pays there, and the plan starts once they have. A workspace that already pays for a plan changes it with `OpenPortalAsync` instead.

Nothing is paid through the API: the person pays, changes the card or cancels on the billing provider's page the link opens, as they do from the app.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:write`.

**Parameters**

- `plan` (`string`, required): `starter`, `business` or `enterprise`, as in `OpenEmail\Constants\PaidPlanIds`.
- `interval` (`string`): `month` or `year`, as in `OpenEmail\Constants\BillingIntervals`. Defaults to `month`.
- `theme` (`string`): `light` or `dark`, for the page the link opens, as in `OpenEmail\Constants\BillingThemes`.
- `locale` (`string`): A language tag such as `de` or `pt-PT`, for the page the link opens.
- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `url`.

**Example**

```csharp
using OpenEmail.Constants;

var checkout = await client.Billing.StartCheckoutAsync(new Body
{
    ["plan"] = PaidPlanIds.Business,
    ["interval"] = BillingIntervals.Year,
    ["locale"] = "de",
});

Console.WriteLine($"Pay here: {checkout["url"]}");
```

**Notes**

- With an OAuth access token it asks for a verification code: until the app has verified one, it is refused with 403 `step_up_required`. An API key is never asked.
- A workspace that already pays for a plan is 409 `plan_conflict`, and a plan or interval not sold on the install is 422 `plan_unavailable`.
- The SDK does not retry it.

Also available in: API [`POST /billing/checkout`](https://openemail.uk/docs/api/reference/billing#post-billing-checkout); TypeScript [`billing.startCheckout()`](https://openemail.uk/docs/sdk/reference/billing#startCheckout); Python [`billing.start_checkout()`](https://openemail.uk/docs/python/reference/billing#startCheckout); Ruby [`billing.start_checkout`](https://openemail.uk/docs/ruby/reference/billing#startCheckout); PHP [`billing->startCheckout`](https://openemail.uk/docs/php/reference/billing#startCheckout); Go [`Billing.StartCheckout`](https://openemail.uk/docs/go/reference/billing#startCheckout); Java [`billing().startCheckout`](https://openemail.uk/docs/java/reference/billing#startCheckout); CLI [`openemail billing start-checkout`](https://openemail.uk/docs/cli/reference/billing#billing-start-checkout).

### `Billing.OpenPortalAsync`

Open the billing portal

```csharp
Task<JsonObject> OpenPortalAsync(
    IReadOnlyDictionary<string, object?>? body = null,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns a link to the billing portal, where the person changes or cancels the plan, updates the card and downloads past invoices, as Manage does in the app.

Nothing is paid through the API: the person pays, changes the card or cancels on the billing provider's page the link opens, as they do from the app.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:write`.

**Parameters**

- `theme` (`string`): `light` or `dark`, for the page the link opens, as in `OpenEmail\Constants\BillingThemes`.
- `locale` (`string`): A language tag such as `de` or `pt-PT`, for the page the link opens.
- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `url`.

**Example**

```csharp
var portal = await client.Billing.OpenPortalAsync(body: new Body { ["theme"] = "dark" });

Console.WriteLine($"Manage the plan here: {portal["url"]}");
```

**Notes**

- With an OAuth access token it asks for a verification code: until the app has verified one, it is refused with 403 `step_up_required`. An API key is never asked.
- The SDK does not retry it.

Also available in: API [`POST /billing/portal`](https://openemail.uk/docs/api/reference/billing#post-billing-portal); TypeScript [`billing.openPortal()`](https://openemail.uk/docs/sdk/reference/billing#openPortal); Python [`billing.open_portal()`](https://openemail.uk/docs/python/reference/billing#openPortal); Ruby [`billing.open_portal`](https://openemail.uk/docs/ruby/reference/billing#openPortal); PHP [`billing->openPortal`](https://openemail.uk/docs/php/reference/billing#openPortal); Go [`Billing.OpenPortal`](https://openemail.uk/docs/go/reference/billing#openPortal); Java [`billing().openPortal`](https://openemail.uk/docs/java/reference/billing#openPortal); CLI [`openemail billing open-portal`](https://openemail.uk/docs/cli/reference/billing#billing-open-portal).

### `Billing.ListInvoicesAsync`

List the charges

```csharp
Task<JsonObject> ListInvoicesAsync(
    int? page = null,
    int? limit = null,
    string? sort = null,
    string? search = null,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns every charge of the workspace with the state of its invoice, a page at a time, as History on Plans & Billing lists them. `search:` matches the plan, the invoice number, the charge id and the name billed.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `page` (`int?`): Which page, from 1.
- `limit` (`int?`): Charges per page, from 1 to 100. The server defaults to 25.
- `sort` (`string?`): `newest`, the default, or `oldest`, as in `OpenEmail\Constants\BillingInvoiceSorts`.
- `search` (`string?`): Words to look for, up to 120 characters.
- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `data`, `total`, `page`, `limit`, `hasMore` and `metered`.

**Example**

```csharp
var invoices = await client.Billing.ListInvoicesAsync(limit: 10);

foreach (var charge in invoices["data"]?.AsArray() ?? [])
{
    if ((bool?)charge?["paid"] != true)
    {
        Console.WriteLine($"{charge?["id"]} {charge?["amountCents"]} cents, {charge?["status"]}");
    }
}

Console.WriteLine($"{((bool?)invoices["hasMore"] == true ? "More on page 2" : "That is every charge")}");
```

**Notes**

- A provider that does not answer is 502 `billing_unreachable`.

Also available in: API [`GET /billing/invoices`](https://openemail.uk/docs/api/reference/billing#get-billing-invoices); TypeScript [`billing.listInvoices()`](https://openemail.uk/docs/sdk/reference/billing#listInvoices); Python [`billing.list_invoices()`](https://openemail.uk/docs/python/reference/billing#listInvoices); Ruby [`billing.list_invoices`](https://openemail.uk/docs/ruby/reference/billing#listInvoices); PHP [`billing->listInvoices`](https://openemail.uk/docs/php/reference/billing#listInvoices); Go [`Billing.ListInvoices`](https://openemail.uk/docs/go/reference/billing#listInvoices); Java [`billing().listInvoices`](https://openemail.uk/docs/java/reference/billing#listInvoices); CLI [`openemail billing list-invoices`](https://openemail.uk/docs/cli/reference/billing#billing-list-invoices).

### `Billing.GetInvoiceAsync`

Get the invoice of a charge

```csharp
Task<JsonObject> GetInvoiceAsync(
    string orderId,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns the link to the invoice of one charge, as a PDF, and its file name. An invoice that was not written yet is written first, which can take a few seconds. The link is signed by the billing provider, downloads the PDF and stops working after a while, so fetch it again rather than keeping it.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `orderId` (`string`, required): The charge id, as `ListInvoicesAsync` returns it.
- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `url` and `fileName`.

**Example**

```csharp
var invoices = await client.Billing.ListInvoicesAsync(limit: 1);

if (invoices["data"]?.AsArray().FirstOrDefault() is { } latest)
{
    var invoice = await client.Billing.GetInvoiceAsync(latest["id"]!.GetValue<string>());

    Console.WriteLine($"{invoice["fileName"]}: {invoice["url"]}");
}
```

**Notes**

- A charge with no billing name and address is 409 `billing_details_required`: save them with `SaveInvoiceDetailsAsync`. A draft or void charge is 409 `invoice_unavailable`, and an invoice still being written is 504 `invoice_not_ready`.

Also available in: API [`GET /billing/invoices/{orderId}`](https://openemail.uk/docs/api/reference/billing#get-billing-invoices-orderid); TypeScript [`billing.getInvoice()`](https://openemail.uk/docs/sdk/reference/billing#getInvoice); Python [`billing.get_invoice()`](https://openemail.uk/docs/python/reference/billing#getInvoice); Ruby [`billing.get_invoice`](https://openemail.uk/docs/ruby/reference/billing#getInvoice); PHP [`billing->getInvoice`](https://openemail.uk/docs/php/reference/billing#getInvoice); Go [`Billing.GetInvoice`](https://openemail.uk/docs/go/reference/billing#getInvoice); Java [`billing().getInvoice`](https://openemail.uk/docs/java/reference/billing#getInvoice); CLI [`openemail billing get-invoice`](https://openemail.uk/docs/cli/reference/billing#billing-get-invoice).

### `Billing.SaveInvoiceDetailsAsync`

Save the billing details of a charge

```csharp
Task<JsonObject> SaveInvoiceDetailsAsync(
    string orderId,
    IReadOnlyDictionary<string, object?> body,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Saves the name and address billed on a charge, as the Billing address dialog does, keeps the address for later charges, and starts writing its invoice. `invoice` is `ready` when the charge already had one, `started` when it is being written, and `refused` with the reason in `message`.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:write`.

**Parameters**

- `orderId` (`string`, required): The charge id, as `ListInvoicesAsync` returns it.
- `name` (`string`, required): Who is billed, up to 200 characters.
- `line1` (`string`, required): The street address, up to 200 characters.
- `line2` (`string`): A flat, suite or building, up to 200 characters.
- `city` (`string`, required): Up to 120 characters.
- `postalCode` (`string`): Up to 40 characters.
- `state` (`string`): The state or region, up to 60 characters. In the US and Canada, its two-letter code.
- `country` (`string`, required): A two-letter ISO 3166 code, one of those `ListCountriesAsync` returns.
- `apiKey` (`string?`): Overrides the client's API key for this call only.
- `cancellationToken` (`CancellationToken`): Cancels the request.

**Returns**

A `JsonObject` with `invoice` and `message`.

**Example**

```csharp
var result = await client.Billing.SaveInvoiceDetailsAsync("3b9f2c4e-7a1d-4e8b-9c0f-5d6a7b8c9d0e", new Body
{
    ["name"] = "Acme Ltd",
    ["line1"] = "1 Market Street",
    ["city"] = "London",
    ["postalCode"] = "EC1A 1AA",
    ["country"] = "GB",
});

Console.WriteLine($"Invoice {result["invoice"]}");
```

**Notes**

- With an OAuth access token it asks for a verification code: until the app has verified one, it is refused with 403 `step_up_required`. An API key is never asked.
- An address the billing provider refuses is 422 `billing_address_rejected`.
- The SDK does not retry it.

Also available in: API [`PUT /billing/invoices/{orderId}/details`](https://openemail.uk/docs/api/reference/billing#put-billing-invoices-orderid-details); TypeScript [`billing.saveInvoiceDetails()`](https://openemail.uk/docs/sdk/reference/billing#saveInvoiceDetails); Python [`billing.save_invoice_details()`](https://openemail.uk/docs/python/reference/billing#saveInvoiceDetails); Ruby [`billing.save_invoice_details`](https://openemail.uk/docs/ruby/reference/billing#saveInvoiceDetails); PHP [`billing->saveInvoiceDetails`](https://openemail.uk/docs/php/reference/billing#saveInvoiceDetails); Go [`Billing.SaveInvoiceDetails`](https://openemail.uk/docs/go/reference/billing#saveInvoiceDetails); Java [`billing().saveInvoiceDetails`](https://openemail.uk/docs/java/reference/billing#saveInvoiceDetails); CLI [`openemail billing save-invoice-details`](https://openemail.uk/docs/cli/reference/billing#billing-save-invoice-details).

### `Billing.ListAddOnsAsync`

List add-ons

```csharp
Task<JsonObject> ListAddOnsAsync(
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Returns what can be bought on top of the plan, today the dedicated sending IP: whether it is for sale, its price per IP a month in `priceCents`, whether the workspace has it and when it ends, the IPs it brought, and `eligibility`, which says whether the workspace qualifies. A dedicated IP needs the Business or Enterprise plan and at least 100,000 emails sent in a month, because below that volume it delivers worse than the shared IPs.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:read`.

**Parameters**

- `cancellationToken` (`CancellationToken`): Cancels the request.
- `apiKey` (`string?`): Overrides the client's API key for this call only.

**Returns**

A `JsonObject` with one object per add-on in `data`: `addOn`, `available`, `status`, `priceCents`, `endsAt`, `eligibility` and `ips`.

**Example**

```csharp
var addOns = await client.Billing.ListAddOnsAsync();

foreach (var addOn in addOns["data"]?.AsArray() ?? [])
{
    Console.WriteLine($"{addOn?["addOn"]}: {addOn?["status"]?.ToString() ?? "not bought"}, {addOn?["priceCents"]} cents a month");
}
```

**Notes**

- `status` is null until the workspace has the add-on. `pending` is paid and being set up, `warming` and `active` send through the dedicated IP, and `releasing` is being taken down.
- A GET is retried automatically on network failure and on 408, 500, 502, 503 and 504 responses, up to the client `MaxRetries`, and on a 429 only when it carries a `Retry-After` of a minute or less.

Also available in: API [`GET /billing/add-ons`](https://openemail.uk/docs/api/reference/billing#get-billing-add-ons); TypeScript [`billing.listAddOns()`](https://openemail.uk/docs/sdk/reference/billing#listAddOns); Python [`billing.list_add_ons()`](https://openemail.uk/docs/python/reference/billing#listAddOns); Ruby [`billing.list_add_ons`](https://openemail.uk/docs/ruby/reference/billing#listAddOns); PHP [`billing->listAddOns`](https://openemail.uk/docs/php/reference/billing#listAddOns); Go [`Billing.ListAddOns`](https://openemail.uk/docs/go/reference/billing#listAddOns); Java [`billing().listAddOns`](https://openemail.uk/docs/java/reference/billing#listAddOns); CLI [`openemail billing list-add-ons`](https://openemail.uk/docs/cli/reference/billing#billing-list-add-ons).

### `Billing.StartAddOnCheckoutAsync`

Start a checkout for an add-on

```csharp
Task<JsonObject> StartAddOnCheckoutAsync(
    IReadOnlyDictionary<string, object?> body,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Starts buying an add-on, as the button under Plans & Billing does, and returns the link to the checkout. Nothing is charged until the person pays there. Once it is paid the dedicated IP is set up and warmed up, which `Sending.ListDedicatedIpsAsync` shows.

Nothing is paid through the API: the person pays on the billing provider's page the link opens, as they do from the app.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:write`.

**Parameters**

- `addOn` (`string`, required): The add-on to buy: `dedicated-ip`.
- `theme` (`string`): `light` or `dark`, for the page the link opens.
- `locale` (`string`): A language tag such as `de` or `pt-PT`, for the page the link opens.
- `cancellationToken` (`CancellationToken`): Cancels the request.
- `apiKey` (`string?`): Overrides the client's API key for this call only.

**Returns**

A `JsonObject` with `addOn` and `url`.

**Example**

```csharp
using OpenEmail.Constants;

var checkout = await client.Billing.StartAddOnCheckoutAsync(new Body { ["addOn"] = AddOns.DedicatedIp, ["theme"] = BillingThemes.Dark });

Console.WriteLine($"Pay for {checkout["addOn"]} at {checkout["url"]}");
```

**Notes**

- With an OAuth access token it asks for a verification code: until the app has verified one, it is refused with 403 `step_up_required`. An API key is never asked.
- A workspace that already has the add-on is 409 `add_on_already_active`, one that does not qualify is 409 `add_on_not_eligible`, and when every IP is taken it is 409 `add_on_sold_out`. An install that does not sell add-ons is 503 `add_on_unavailable`.
- The SDK does not retry it.

Also available in: API [`POST /billing/add-ons/checkout`](https://openemail.uk/docs/api/reference/billing#post-billing-add-ons-checkout); TypeScript [`billing.startAddOnCheckout()`](https://openemail.uk/docs/sdk/reference/billing#startAddOnCheckout); Python [`billing.start_add_on_checkout()`](https://openemail.uk/docs/python/reference/billing#startAddOnCheckout); Ruby [`billing.start_add_on_checkout`](https://openemail.uk/docs/ruby/reference/billing#startAddOnCheckout); PHP [`billing->startAddOnCheckout`](https://openemail.uk/docs/php/reference/billing#startAddOnCheckout); Go [`Billing.StartAddOnCheckout`](https://openemail.uk/docs/go/reference/billing#startAddOnCheckout); Java [`billing().startAddOnCheckout`](https://openemail.uk/docs/java/reference/billing#startAddOnCheckout); CLI [`openemail billing start-add-on-checkout`](https://openemail.uk/docs/cli/reference/billing#billing-start-add-on-checkout).

### `Billing.CancelAddOnAsync`

Cancel an add-on

```csharp
Task<JsonObject> CancelAddOnAsync(
    string addOn,
    string? apiKey = null,
    CancellationToken cancellationToken = default)
```

Cancels an add-on. It stays until the end of the month already paid for, which `endsAt` gives. Then the dedicated IP is released and mail leaves through the shared IPs again.

Billing belongs to the owner of the workspace: an API key acts for the owner, and an OAuth access token reaches billing only when the owner connected the app with every address. A token that acts for a member is 403 `owner_only`, and a key or token limited to particular addresses or domains is 422 `capability_unsupported`.

Scopes: `billing:write`.

**Parameters**

- `addOn` (`string`, required): The add-on to cancel: `dedicated-ip`.
- `cancellationToken` (`CancellationToken`): Cancels the request.
- `apiKey` (`string?`): Overrides the client's API key for this call only.

**Returns**

A `JsonObject`, now with `endsAt` set.

**Example**

```csharp
using OpenEmail.Constants;

var addOn = await client.Billing.CancelAddOnAsync(AddOns.DedicatedIp);

Console.WriteLine($"{addOn["addOn"]} ends {addOn["endsAt"]}");
```

**Notes**

- With an OAuth access token it asks for a verification code: until the app has verified one, it is refused with 403 `step_up_required`. An API key is never asked.
- A workspace that does not have the add-on, or whose add-on is already ending, is 409 `add_on_not_active`.
- The SDK does not retry a delete.

Also available in: API [`DELETE /billing/add-ons/{addOn}`](https://openemail.uk/docs/api/reference/billing#delete-billing-add-ons-addon); TypeScript [`billing.cancelAddOn()`](https://openemail.uk/docs/sdk/reference/billing#cancelAddOn); Python [`billing.cancel_add_on()`](https://openemail.uk/docs/python/reference/billing#cancelAddOn); Ruby [`billing.cancel_add_on`](https://openemail.uk/docs/ruby/reference/billing#cancelAddOn); PHP [`billing->cancelAddOn`](https://openemail.uk/docs/php/reference/billing#cancelAddOn); Go [`Billing.CancelAddOn`](https://openemail.uk/docs/go/reference/billing#cancelAddOn); Java [`billing().cancelAddOn`](https://openemail.uk/docs/java/reference/billing#cancelAddOn); CLI [`openemail billing cancel-add-on`](https://openemail.uk/docs/cli/reference/billing#billing-cancel-add-on).
