# Binadit API met JavaScript / TypeScript

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

Node.js 22+. Een ES-module voor servers; ook vanuit TypeScript te importeren. Stop servicegeheimen nooit in browserbundels.

## 1. Herbruikbare client

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

```javascript
// Node.js 22+, server-side JavaScript. Never put service secrets in browser code.
import { createHmac, timingSafeEqual } from 'node:crypto';
const ORIGIN = 'https://my.binadit.com';
const LIMIT = 8 * 1024 * 1024;

async function send(method, path, headers, body) {
  if (!(path.startsWith('/api/v1/') || path === '/oauth/token') || /[\\#]/.test(path)) {
    throw new Error('Use a relative Binadit API path');
  }
  let response;
  try {
    response = await fetch(ORIGIN + path, {
      method, headers, body, redirect: 'manual', signal: AbortSignal.timeout(30000),
    });
  } catch {
    throw new Error('Transport outcome unknown; preserve the original request');
  }
  if (!response.ok) {
    await response.body?.cancel();
    throw new Error(`API HTTP ${response.status}; inspect the original operation`);
  }
  const chunks = [];
  let size = 0;
  for await (const chunk of response.body ?? []) {
    size += chunk.length;
    if (size > LIMIT) throw new Error("Response exceeds the example's 8 MiB limit");
    chunks.push(Buffer.from(chunk));
  }
  return { headers: response.headers, bytes: Buffer.concat(chunks) };
}

export async function accessToken() {
  if (process.env.BINADIT_ACCESS_TOKEN) return process.env.BINADIT_ACCESS_TOKEN;
  const { BINADIT_CLIENT_ID: id, BINADIT_CLIENT_SECRET: secret } = process.env;
  if (!id || !secret) throw new Error('Supply service credentials from your secret manager');
  const result = await send('POST', '/oauth/token', {
    Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded',
  }, new URLSearchParams({ grant_type: 'client_credentials', client_id: id, client_secret: secret }));
  const token = JSON.parse(result.bytes.toString('utf8')).access_token;
  if (typeof token !== 'string' || !token) throw new Error('Token response is invalid');
  return token;
}

export async function api(token, method, path, body = undefined, options = {}) {
  if (!path.startsWith('/api/v1/') || !['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
    throw new Error('Use a supported API method and path');
  }
  const headers = { Authorization: `Bearer ${token}`, Accept: options.binary ? 'application/pdf' : 'application/json' };
  if (method !== 'GET') {
    if (!/^[A-Za-z0-9_.:-]{16,128}$/.test(options.key ?? '')) throw new Error('Supply the persisted original Idempotency-Key');
    headers['Idempotency-Key'] = options.key;
  }
  if (options.version) headers['If-Match'] = options.version;
  if (body !== undefined) headers['Content-Type'] = 'application/json';
  const result = await send(method, path, headers, body === undefined ? undefined : JSON.stringify(body));
  if (options.binary && result.headers.get('content-type')?.split(';')[0] !== 'application/pdf') throw new Error('Expected an invoice PDF');
  return { etag: result.headers.get('etag'), data: options.binary ? result.bytes : (result.bytes.length ? JSON.parse(result.bytes.toString('utf8')) : null) };
}

export function verifyWebhook(raw, deliveryId, timestamp, signature, expectedKeyId, secret, now = Date.now() / 1000) {
  const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
  if (raw.length > 1024 * 1024 || secret.length < 32 || !uuid.test(deliveryId) || !uuid.test(expectedKeyId)
      || !/^[1-9][0-9]{8,10}$/.test(timestamp) || Math.abs(now - Number(timestamp)) > 300) return false;
  const digest = createHmac('sha256', secret).update(`${timestamp}.${deliveryId}.`).update(raw).digest('hex');
  const expected = Buffer.from(`v1=${digest},key_id=${expectedKeyId}`);
  const actual = Buffer.from(signature);
  return actual.length === expected.length && timingSafeEqual(actual, expected);
}
```

## 2. Eenmalig voor deze workflow

```javascript
import { accessToken, api } from './binadit.mjs';
const token = await 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`

```javascript
const result = await api(token, "GET", "/api/v1/me");
```

## Bedrijven ophalen

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

```javascript
const result = await api(token, "GET", "/api/v1/accounts");
```

## Beschikbaarheid en scopes controleren

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

```javascript
const result = await api(token, "GET", "/api/v1/accounts/123/capabilities");
```

## Bedrijfsgegevens en ETag lezen

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

```javascript
const result = await 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.

```javascript
const result = await api(token, "PATCH", "/api/v1/accounts/123/profile", {"city": "Utrecht", "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.BINADIT_REQUEST_KEY });
```

## Producten en prijzen bekijken

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

```javascript
const result = await api(token, "GET", "/api/v1/accounts/123/products");
```

## Bestaande domeinhouders ophalen

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

```javascript
const result = await 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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/holders/default", {"dry_run": true}, { key: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/holders", {"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: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/domains/check", {"domains": ["example.test"]}, { key: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/order-quotes", {"kind": "domain_register", "input": {"domain": "example.test", "product_id": 9, "holder_id": 17}, "dry_run": true}, { key: process.env.BINADIT_REQUEST_KEY });
```

## Een bewaarde offerte opnieuw lezen

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

```javascript
const result = await 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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/orders", {"quote_id": "11111111-1111-4111-8111-111111111111", "dry_run": true}, { key: process.env.BINADIT_REQUEST_KEY });
```

## Registratie, verlenging en abonnementstatus lezen

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

```javascript
const result = await 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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/domains/31/actions/cancel", {"confirm_domain": "example.test", "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.BINADIT_REQUEST_KEY });
```

## DNS-zones ophalen

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

```javascript
const result = await 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`

```javascript
const result = await 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`

```javascript
const result = await 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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/dns/templates", {"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: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/dns/zones/8/template-plans", {"template_id": 11, "dry_run": true}, { key: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/dns/change-plans", {"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: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/dns/change-plans/11111111-1111-4111-8111-111111111111/apply", {"dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.BINADIT_REQUEST_KEY });
```

## Een oorspronkelijke operatie volgen

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

```javascript
const result = await 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`

```javascript
const result = await 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`

```javascript
const result = await api(token, "GET", "/api/v1/accounts/123/billing/invoices/45/pdf", undefined, { binary: true });
```

`result.data` 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`

```javascript
const result = await api(token, "GET", "/api/v1/accounts/123/workspaces");
```

## Mailboxen ophalen

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

```javascript
const result = await 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`

```javascript
const result = await 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.

```javascript
const result = await api(token, "PUT", "/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24", {"level": "full", "send_as": false, "calendar_level": "write", "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/workspaces/7/mailing-lists", {"domain_id": 12, "local_part": "team", "display_name": "Team", "members": [{"kind": "mailbox", "id": 23}, {"kind": "mailbox", "id": 24}], "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "PUT", "/api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24", {"dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "DELETE", "/api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24", {"dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/workspaces/7/rooms", {"domain_id": 12, "local_part": "vergaderzaal", "display_name": "Vergaderzaal 1", "kind": "room", "capacity": 8, "location": "Eerste verdieping", "manager_ids": [23], "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "PUT", "/api/v1/accounts/123/workspaces/7/rooms/11/managers/24", {"dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "DELETE", "/api/v1/accounts/123/workspaces/7/rooms/11", {"confirm_address": "vergaderzaal@example.test", "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.BINADIT_REQUEST_KEY });
```

## Hostingabonnementen ophalen

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

```javascript
const result = await 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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/hosting/subscriptions/41/actions/cancel", {"intent_id": "33333333-3333-4333-8333-333333333333", "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.BINADIT_REQUEST_KEY });
```

## Spamfilters ophalen

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

```javascript
const result = await 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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/team/invitations", {"email": "collega@example.test", "role": "member", "permissions": [{"module": "module_dns", "level": "read"}], "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/webhooks", {"name": "Orderstatus", "url": "https://receiver.example.test/binadit", "integration_id": "44444444-4444-4444-8444-444444444444", "dry_run": true}, { key: process.env.BINADIT_REQUEST_KEY });
```

## Webhookafleveringen inspecteren

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

```javascript
const result = await 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`

```javascript
const result = await 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.

```javascript
const result = await api(token, "POST", "/api/v1/accounts/123/order-quotes", {"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: process.env.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.

```javascript
const payload = {"domain_id": 12, "product_id": 9, "local_part": "support", "display_name": "Support", "member_ids": [23, 24], "dry_run": true};
payload["product_version"] = process.env.BINADIT_PRODUCT_VERSION;
const result = await api(token, "POST", "/api/v1/accounts/123/workspaces/7/shared-mailboxes", payload, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "PUT", "/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24", {"level": "full", "send_as": true, "calendar_level": "write", "dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.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.

```javascript
const result = await api(token, "DELETE", "/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24", {"dry_run": true}, { version: process.env.BINADIT_RESOURCE_VERSION, key: process.env.BINADIT_REQUEST_KEY });
```

## Verder werken

### Pagineren zonder resources over te slaan

Controleer `meta.next_cursor` ook als `data` leeg is. Volg geen externe next-URL met een token.

```javascript
let cursor = null;
const seen = new Set();
for (let page = 0; page < 20; page++) {
  const query = new URLSearchParams({ limit: '25' });
  if (cursor !== null) query.set('cursor', String(cursor));
  const result = await api(token, 'GET', `/api/v1/accounts/123/domains?${query}`);
  for (const domain of result.data.data) console.log(domain.id);
  cursor = result.data.meta?.next_cursor ?? null;
  if (cursor === null) break;
  if (seen.has(String(cursor))) throw new Error('Repeated pagination cursor');
  seen.add(String(cursor));
  if (page === 19) throw new Error('Page limit reached; explicitly continue from the saved cursor');
}
```

### PDF opslaan zonder overschrijven

```javascript
import { open } from 'node:fs/promises';
const result = await api(token, 'GET', '/api/v1/accounts/123/billing/invoices/45/pdf', undefined, { binary: true });
const file = await open('factuur-45.pdf', 'wx', 0o600);
try { await file.writeFile(result.data); } finally { await file.close(); }
```

Gebruik op Windows een privémap met passende ACL. Log geen PDF-bytes.

### Webhookhandtekening controleren

```javascript
import { verifyWebhook } from './binadit.mjs';
// rawBody must be the unmodified Buffer, before JSON parsing middleware.
const valid = verifyWebhook(rawBody, deliveryIdHeader, timestampHeader, signatureHeader,
  process.env.BINADIT_WEBHOOK_KEY_ID, process.env.BINADIT_WEBHOOK_SECRET);
if (!valid) throw new Error('Reject this webhook');
// Next: atomically insert deliveryIdHeader (UNIQUE) and enqueue work in your DB.
```

Controleer dat headers en secrets aanwezig zijn voordat je de functie aanroept. Gebruik bodylimieten en weiger dubbele headers. De HMAC-controle vergelijkt constant-time en accepteert maximaal vijf minuten tijdverschil. Zij maakt geen werkitem en voorkomt zelf geen replay. Leg de unieke delivery-ID en het werkitem atomair vast voordat je 2xx antwoordt. Voor TypeScript kun je deze server-ES-module met `allowJs` in een bestaand Node-project opnemen; de voorbeelden zijn geen browser-SDK.
