# Binadit API met C#

[Alle talen en veiligheidsafspraken](/developers/guides/code-examples?lang=nl) · [API-referentie](/developers/api?lang=nl#reference)

.NET 10. Maak met dotnet new console een project en voeg Binadit.cs toe. Gebruik de aanroepen hieronder vanuit de async Program.cs.

## 1. Herbruikbare client

Bewaar dit als `Binadit.cs`. De client volgt geen redirects en herhaalt geen aanvragen. Geef geheimen via je secretmanager aan de omgeving door; niets wordt automatisch opgeslagen of gelogd.

```csharp
// .NET 10, standard library only. Reuse one client; no automatic token refresh or retry.
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using System.Security.Cryptography;

public sealed record BinaditReply(string? ETag, JsonNode? Data, byte[] Bytes);

public sealed class BinaditExample : IDisposable
{
    private readonly HttpClient http = new(new HttpClientHandler { AllowAutoRedirect = false, UseCookies = false })
    { Timeout = TimeSpan.FromSeconds(30), MaxResponseContentBufferSize = 8 * 1024 * 1024 };
    private const string Origin = "https://my.binadit.com";

    private async Task<(HttpResponseMessage Response, byte[] Bytes)> Send(string method, string path, string? body, string contentType, string? token = null, string? version = null, string? key = null, bool binary = false)
    {
        if ((!path.StartsWith("/api/v1/", StringComparison.Ordinal) && path != "/oauth/token") || path.Contains('\\') || path.Contains('#'))
            throw new ArgumentException("Use a relative Binadit API path");
        using var request = new HttpRequestMessage(new HttpMethod(method), Origin + path);
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(binary ? "application/pdf" : "application/json"));
        if (token is not null) request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        if (version is not null) request.Headers.Add("If-Match", version);
        if (key is not null) request.Headers.Add("Idempotency-Key", key);
        if (body is not null) request.Content = new StringContent(body, Encoding.UTF8, contentType);
        HttpResponseMessage response;
        try { response = await http.SendAsync(request); }
        catch (HttpRequestException) { throw new InvalidOperationException("Transport outcome unknown; preserve the original request"); }
        catch (TaskCanceledException) { throw new InvalidOperationException("Transport outcome unknown; preserve the original request"); }
        if (!response.IsSuccessStatusCode) { int code = (int)response.StatusCode; response.Dispose(); throw new InvalidOperationException($"API HTTP {code}; inspect the original operation"); }
        return (response, await response.Content.ReadAsByteArrayAsync());
    }

    public async Task<string> AccessToken()
    {
        if (Environment.GetEnvironmentVariable("BINADIT_ACCESS_TOKEN") is { Length: > 0 } token) return token;
        string Required(string name) => Environment.GetEnvironmentVariable(name) ?? throw new InvalidOperationException("Supply service credentials from your secret manager");
        using var form = new FormUrlEncodedContent(new Dictionary<string, string> {
            ["grant_type"] = "client_credentials", ["client_id"] = Required("BINADIT_CLIENT_ID"), ["client_secret"] = Required("BINADIT_CLIENT_SECRET") });
        var (response, bytes) = await Send("POST", "/oauth/token", await form.ReadAsStringAsync(), "application/x-www-form-urlencoded");
        using (response) return JsonNode.Parse(bytes)?["access_token"]?.GetValue<string>() is { Length: > 0 } value ? value : throw new InvalidOperationException("Token response is invalid");
    }

    public async Task<BinaditReply> Api(string token, string method, string path, JsonNode? body = null, string? version = null, string? key = null, bool binary = false)
    {
        if (!path.StartsWith("/api/v1/", StringComparison.Ordinal) || !new[] { "GET", "POST", "PUT", "PATCH", "DELETE" }.Contains(method)) throw new ArgumentException("Use a supported API method and path");
        if (method != "GET" && !Regex.IsMatch(key ?? "", @"\A[A-Za-z0-9_.:-]{16,128}\z")) throw new ArgumentException("Supply the persisted original Idempotency-Key");
        var (response, bytes) = await Send(method, path, body?.ToJsonString(), "application/json", token, version, key, binary);
        using (response)
        {
            if (binary && response.Content.Headers.ContentType?.MediaType != "application/pdf") throw new InvalidOperationException("Expected an invoice PDF");
            return new(response.Headers.ETag?.ToString(), binary || bytes.Length == 0 ? null : JsonNode.Parse(bytes), binary ? bytes : []);
        }
    }
    public void Dispose() => http.Dispose();

    public static bool VerifyWebhook(byte[] raw, string deliveryId, string timestamp, string signature, string expectedKeyId, string secret, long? now = null)
    {
        const string uuid = @"\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z";
        if (raw.Length > 1024 * 1024 || secret.Length < 32 || !Regex.IsMatch(deliveryId, uuid) || !Regex.IsMatch(expectedKeyId, uuid)
            || !Regex.IsMatch(timestamp, @"\A[1-9][0-9]{8,10}\z") || Math.Abs((now ?? DateTimeOffset.UtcNow.ToUnixTimeSeconds()) - long.Parse(timestamp)) > 300) return false;
        byte[] input = Encoding.UTF8.GetBytes(timestamp + "." + deliveryId + ".").Concat(raw).ToArray();
        string digest = Convert.ToHexString(HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), input)).ToLowerInvariant();
        return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes("v1=" + digest + ",key_id=" + expectedKeyId), Encoding.UTF8.GetBytes(signature));
    }
}
```

## 2. Eenmalig voor deze workflow

```csharp
using System.Text.Json.Nodes;
using var client = new BinaditExample();
var token = await client.AccessToken();
```

De tokenaanvraag gebeurt één keer voor deze korte workflow. Een servicesleutel is geen menselijke step-up. Stop bij 401 en onderzoek eerst een mogelijk verzonden mutatie. Iedere onderstaande codeblok is een afzonderlijk voorbeeld: plak ze niet allemaal achter elkaar. Alle IDs en example.test-adressen zijn fictief. Schrijfvoorbeelden met mogelijke gevolgen gebruiken dry_run.

## Identiteit controleren

`GET /api/v1/me` · scope `account:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/me");
```

## Bedrijven ophalen

`GET /api/v1/accounts` · scope `account:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts");
```

## Beschikbaarheid en scopes controleren

`GET /api/v1/accounts/123/capabilities` · scope `account:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/capabilities");
```

## Bedrijfsgegevens en ETag lezen

`GET /api/v1/accounts/123/profile` · scope `account:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/profile");
```

## Bedrijfsgegevens wijzigen

`PATCH /api/v1/accounts/123/profile` · scope `account:write`

Lees eerst **GET profile** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "PATCH", "/api/v1/accounts/123/profile", JsonNode.Parse("""{"city": "Utrecht", "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Producten en prijzen bekijken

`GET /api/v1/accounts/123/products` · scope `products:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/products");
```

## Bestaande domeinhouders ophalen

`GET /api/v1/accounts/123/holders` · scope `holders:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/holders");
```

## Standaardhouder uit bedrijfs- of particuliere gegevens voorbereiden

`POST /api/v1/accounts/123/holders/default` · scope `holders:read, holders:write`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/holders/default", JsonNode.Parse("""{"dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een eigen domeinhouder aanmaken

`POST /api/v1/accounts/123/holders` · scope `holders:write`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/holders", JsonNode.Parse("""{"initials": "A", "firstname": "Ada", "lastname": "Example", "gender": "F", "street": "Voorbeeldstraat", "number": "1", "zipcode": "1234 AB", "city": "Utrecht", "country": "NL", "email": "ada@example.test", "tel1": "+31", "tel3": "612345678", "dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Beschikbaarheid van een domein opvragen

`POST /api/v1/accounts/123/domains/check` · scope `domains:read`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/domains/check", JsonNode.Parse("""{"domains": ["example.test"]}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Domeinregistratie offreren

`POST /api/v1/accounts/123/order-quotes` · scope `orders:write`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/order-quotes", JsonNode.Parse("""{"kind": "domain_register", "input": {"domain": "example.test", "product_id": 9, "holder_id": 17}, "dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een bewaarde offerte opnieuw lezen

`GET /api/v1/accounts/123/order-quotes/11111111-1111-4111-8111-111111111111` · scope `orders:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/order-quotes/11111111-1111-4111-8111-111111111111");
```

## Bestelling en betaling voorbereiden

`POST /api/v1/accounts/123/orders` · scope `orders:commit`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/orders", JsonNode.Parse("""{"quote_id": "11111111-1111-4111-8111-111111111111", "dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Registratie, verlenging en abonnementstatus lezen

`GET /api/v1/accounts/123/domains/31/subscription` · scope `domains:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/domains/31/subscription");
```

## Domein niet meer automatisch verlengen

`POST /api/v1/accounts/123/domains/31/actions/cancel` · scope `domains:write`

Lees eerst **GET domains/31** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/domains/31/actions/cancel", JsonNode.Parse("""{"confirm_domain": "example.test", "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## DNS-zones ophalen

`GET /api/v1/accounts/123/dns/zones` · scope `dns.zones:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/dns/zones");
```

## Huidige DNS-records en versie ophalen

`GET /api/v1/accounts/123/dns/zones/8/records` · scope `dns.records:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/dns/zones/8/records");
```

## DNS-templates ophalen

`GET /api/v1/accounts/123/dns/templates` · scope `dns.templates:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/dns/templates");
```

## Een DNS-template voorbereiden

`POST /api/v1/accounts/123/dns/templates` · scope `dns.templates:write`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/dns/templates", JsonNode.Parse("""{"name": "Websitevoorbeeld", "replace": "none", "records": [{"name": "@", "type": "A", "value": "192.0.2.50", "ttl": 300}, {"name": "www", "type": "CNAME", "value": "example.test", "ttl": 300}], "dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Toepassen van een template voorstellen

`POST /api/v1/accounts/123/dns/zones/8/template-plans` · scope `dns.records:write`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/dns/zones/8/template-plans", JsonNode.Parse("""{"template_id": 11, "dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Meerdere DNS-records in één plan voorbereiden

`POST /api/v1/accounts/123/dns/change-plans` · scope `dns.records:write`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/dns/change-plans", JsonNode.Parse("""{"zone": "example.test", "changes": [{"action": "create", "name": "proef", "type": "A", "value": "192.0.2.50", "ttl": 300}, {"action": "create", "name": "_verify", "type": "TXT", "value": "binadit-example", "ttl": 300}], "dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een bekeken DNS-plan toepassen

`POST /api/v1/accounts/123/dns/change-plans/11111111-1111-4111-8111-111111111111/apply` · scope `dns.records:write`

Lees eerst **version van het oorspronkelijke bekeken DNS-plan** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/dns/change-plans/11111111-1111-4111-8111-111111111111/apply", JsonNode.Parse("""{"dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een oorspronkelijke operatie volgen

`GET /api/v1/accounts/123/operations/22222222-2222-4222-8222-222222222222` · scope `operations:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/operations/22222222-2222-4222-8222-222222222222");
```

## Facturen ophalen

`GET /api/v1/accounts/123/billing/invoices` · scope `billing.invoices:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/billing/invoices");
```

## Een factuur als PDF ophalen

`GET /api/v1/accounts/123/billing/invoices/45/pdf` · scope `billing.invoices:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/billing/invoices/45/pdf", binary: true);
```

`result.Bytes` bevat uitsluitend PDF-bytes. Gebruik de aparte privé-bestandsvoorbeelden hieronder; print geen PDF- of persoonsgegevens.

## Workspaces ophalen

`GET /api/v1/accounts/123/workspaces` · scope `workspace:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/workspaces");
```

## Mailboxen ophalen

`GET /api/v1/accounts/123/workspaces/7/mailboxes` · scope `workspace.mailboxes:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/workspaces/7/mailboxes");
```

## Gemachtigden en gebruikersdelingen lezen

`GET /api/v1/accounts/123/workspaces/7/mailboxes/23/delegates` · scope `workspace.mailboxes:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates");
```

## Volledige mailboxtoegang en agenda bewerken

`PUT /api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7/mailboxes/23** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "PUT", "/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24", JsonNode.Parse("""{"level": "full", "send_as": false, "calendar_level": "write", "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een gratis verzendlijst voorbereiden

`POST /api/v1/accounts/123/workspaces/7/mailing-lists` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/workspaces/7/mailing-lists", JsonNode.Parse("""{"domain_id": 12, "local_part": "team", "display_name": "Team", "members": [{"kind": "mailbox", "id": 23}, {"kind": "mailbox", "id": 24}], "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Eén lijstlid toevoegen

`PUT /api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7/mailing-lists/10** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "PUT", "/api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24", JsonNode.Parse("""{"dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Eén lijstlid verwijderen

`DELETE /api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7/mailing-lists/10** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "DELETE", "/api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24", JsonNode.Parse("""{"dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een gratis ruimte voorbereiden

`POST /api/v1/accounts/123/workspaces/7/rooms` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/workspaces/7/rooms", JsonNode.Parse("""{"domain_id": 12, "local_part": "vergaderzaal", "display_name": "Vergaderzaal 1", "kind": "room", "capacity": 8, "location": "Eerste verdieping", "manager_ids": [23], "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een ruimtebeheerder toevoegen

`PUT /api/v1/accounts/123/workspaces/7/rooms/11/managers/24` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7/rooms/11** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "PUT", "/api/v1/accounts/123/workspaces/7/rooms/11/managers/24", JsonNode.Parse("""{"dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een ruimte opheffen met persoonlijke verificatie

`DELETE /api/v1/accounts/123/workspaces/7/rooms/11` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7/rooms/11** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

Vereist ook recente sterke menselijke verificatie, actuele bevoegdheden en ingeschakelde gevoelige acties. Een servicesleutel kan die verificatie niet leveren.

```csharp
var result = await client.Api(token, "DELETE", "/api/v1/accounts/123/workspaces/7/rooms/11", JsonNode.Parse("""{"confirm_address": "vergaderzaal@example.test", "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Hostingabonnementen ophalen

`GET /api/v1/accounts/123/hosting/subscriptions` · scope `hosting:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/hosting/subscriptions");
```

## Hostingopzegging voorbereiden

`POST /api/v1/accounts/123/hosting/subscriptions/41/actions/cancel` · scope `hosting:write`

Lees eerst **GET hosting/subscriptions/41** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/hosting/subscriptions/41/actions/cancel", JsonNode.Parse("""{"intent_id": "33333333-3333-4333-8333-333333333333", "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Spamfilters ophalen

`GET /api/v1/accounts/123/spam/filters` · scope `spam:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/spam/filters");
```

## Een collega met beperkte rechten uitnodigen

`POST /api/v1/accounts/123/team/invitations` · scope `team:write`

Lees eerst **GET profile** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

Vereist ook recente sterke menselijke verificatie, actuele bevoegdheden en ingeschakelde gevoelige acties. Een servicesleutel kan die verificatie niet leveren.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/team/invitations", JsonNode.Parse("""{"email": "collega@example.test", "role": "member", "permissions": [{"module": "module_dns", "level": "read"}], "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een gepauzeerde webhook voorbereiden

`POST /api/v1/accounts/123/webhooks` · scope `webhooks:write`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

Vereist ook recente sterke menselijke verificatie, actuele bevoegdheden en ingeschakelde gevoelige acties. Een servicesleutel kan die verificatie niet leveren.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/webhooks", JsonNode.Parse("""{"name": "Orderstatus", "url": "https://receiver.example.test/binadit", "integration_id": "44444444-4444-4444-8444-444444444444", "dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Webhookafleveringen inspecteren

`GET /api/v1/accounts/123/webhooks/55555555-5555-4555-8555-555555555555/deliveries` · scope `webhooks:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/webhooks/55555555-5555-4555-8555-555555555555/deliveries");
```

## De actuele productversie lezen

`GET /api/v1/accounts/123/products/9` · scope `products:read`

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/products/9");
```

## Een betaalde normale mailbox offreren

`POST /api/v1/accounts/123/order-quotes` · scope `orders:write`

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "POST", "/api/v1/accounts/123/order-quotes", JsonNode.Parse("""{"kind": "workspace_mailbox", "input": {"workspace_id": 7, "product_id": 9, "domain_id": 12, "local_part": "ada", "display_name": "Ada", "drive": false}, "dry_run": true}"""), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Een betaalde gedeelde mailbox voorbereiden

`POST /api/v1/accounts/123/workspaces/7/shared-mailboxes` · scope `workspace.mailboxes:write, orders:commit`

Lees eerst **GET workspaces/7** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

Lees ook `GET products/9` en bewaar zijn ongequote `data.version` als `BINADIT_PRODUCT_VERSION`. Deze betaalde gedeelde mailbox vraagt naast mailrechten ook `orders:commit` en actuele bestel- en bestedingsrechten. Een productversie is geen Workspace-versie.

```csharp
var payload = JsonNode.Parse("""{"domain_id": 12, "product_id": 9, "local_part": "support", "display_name": "Support", "member_ids": [23, 24], "dry_run": true}""")!;
payload["product_version"] = Environment.GetEnvironmentVariable("BINADIT_PRODUCT_VERSION") ?? throw new InvalidOperationException("Read the product version first");
var result = await client.Api(token, "POST", "/api/v1/accounts/123/workspaces/7/shared-mailboxes", payload, version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Versturen als afzonderlijk inschakelen

`PUT /api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7/mailboxes/23** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

Send As aanzetten vereist recente sterke persoonlijke verificatie en volledige mailtoegang. Een servicesleutel met alleen de genoemde scope is niet voldoende.

```csharp
var result = await client.Api(token, "PUT", "/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24", JsonNode.Parse("""{"level": "full", "send_as": true, "calendar_level": "write", "dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Mail- en agendatoegang samen intrekken

`DELETE /api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24` · scope `workspace.mailboxes:write`

Lees eerst **GET workspaces/7/mailboxes/23** en bewaar de bijbehorende HTTP ETag (of expliciet beschreven planversie) als `BINADIT_RESOURCE_VERSION`. Versies van verschillende resources zijn niet uitwisselbaar.

Gebruik voor exact deze beoordeelde aanvraag een vooraf duurzaam bewaarde `BINADIT_REQUEST_KEY` van 16–128 veilige tekens. Hergebruik haar alleen voor dezelfde methode, URL en JSON; nooit voor een volgende stap.

```csharp
var result = await client.Api(token, "DELETE", "/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24", JsonNode.Parse("""{"dry_run": true}"""), version: Environment.GetEnvironmentVariable("BINADIT_RESOURCE_VERSION") ?? throw new InvalidOperationException("Read the current ETag first"), key: Environment.GetEnvironmentVariable("BINADIT_REQUEST_KEY"));
```

## Verder werken

### Pagineren zonder resources over te slaan

```csharp
string? cursor = null;
var seen = new HashSet<string>();
for (int page = 0; page < 20; page++)
{
    var path = "/api/v1/accounts/123/domains?limit=25";
    if (cursor is not null) path += "&cursor=" + Uri.EscapeDataString(cursor);
    var result = await client.Api(token, "GET", path);
    foreach (var domain in result.Data!["data"]!.AsArray()) Console.WriteLine(domain!["id"]);
    cursor = result.Data["meta"]?["next_cursor"]?.GetValue<string>();
    if (cursor is null) break;
    if (!seen.Add(cursor)) throw new InvalidOperationException("Repeated pagination cursor");
    if (page == 19) throw new InvalidOperationException("Page limit reached; explicitly continue from the saved cursor");
}
```

Een lege lijst kan nog wel een volgende cursor hebben. Alleen de vertrouwde eigen resourcepath wordt gebruikt; geen externe next-URL.

### PDF in een nieuw privébestand opslaan

```csharp
var result = await client.Api(token, "GET", "/api/v1/accounts/123/billing/invoices/45/pdf", binary: true);
var options = new FileStreamOptions { Mode = FileMode.CreateNew, Access = FileAccess.Write, Share = FileShare.None };
if (!OperatingSystem.IsWindows()) options.UnixCreateMode = UnixFileMode.UserRead | UnixFileMode.UserWrite;
await using var output = new FileStream("factuur-45.pdf", options);
await output.WriteAsync(result.Bytes);
```

Op Windows erft het bestand de map-ACL: kies een map die uitsluitend voor de bedoelde gebruiker toegankelijk is. Log de PDF-bytes niet.

### Webhookhandtekening controleren

```csharp
// Obtain these raw bytes and headers from your server framework before JSON parsing.
bool valid = BinaditExample.VerifyWebhook(rawBody, deliveryIdHeader, timestampHeader, signatureHeader,
    Environment.GetEnvironmentVariable("BINADIT_WEBHOOK_KEY_ID") ?? "",
    Environment.GetEnvironmentVariable("BINADIT_WEBHOOK_SECRET") ?? "");
if (!valid) throw new InvalidOperationException("Reject this webhook");
// Next: atomically insert the delivery ID (UNIQUE) and enqueue work in your DB.
```

De volledige helper hierboven gebruikt `HMACSHA256` en `CryptographicOperations.FixedTimeEquals`. Configureer een bodylimiet en valideer ontbrekende en dubbele headers in je framework. Sla de delivery-ID en het werkitem in één transactie op vóór het 2xx-antwoord. Een geldig al opgeslagen bericht krijgt opnieuw 2xx zonder dubbele uitvoering. Deze helper is geen kant-en-klare webhookserver en maakt geen duurzame replayopslag.
