# Binadit API met PHP

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

PHP 8.2+ met de cURL-extensie.

## 1. Herbruikbare client

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

```php
<?php

/** PHP 8.2+ with cURL. No requests or secret output when included. */
final class BinaditExample
{
    private const ORIGIN = 'https://my.binadit.com';

    private static function send(string $method, string $path, array $headers, ?string $body = null): array
    {
        if ((! str_starts_with($path, '/api/v1/') && $path !== '/oauth/token') || strpbrk($path, '\\#') !== false) {
            throw new RuntimeException('Use a relative Binadit API path');
        }
        $responseHeaders = [];
        $raw = '';
        $curl = curl_init(self::ORIGIN.$path);
        curl_setopt_array($curl, [CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => $headers,
            CURLOPT_FOLLOWLOCATION => false, CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
            CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 30,
            CURLOPT_HEADERFUNCTION => static function ($ch, $line) use (&$responseHeaders) {
                if (str_contains($line, ':')) {
                    [$k, $v] = explode(':', $line, 2);
                    $responseHeaders[strtolower(trim($k))] = trim($v);
                }

                return strlen($line);
            },
            CURLOPT_WRITEFUNCTION => static function ($ch, $part) use (&$raw) {
                if (strlen($raw) + strlen($part) > 8 * 1024 * 1024) {
                    return 0;
                }
                $raw .= $part;

                return strlen($part);
            }]);
        if ($body !== null) {
            curl_setopt($curl, CURLOPT_POSTFIELDS, $body);
        }
        $ok = curl_exec($curl);
        $status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
        curl_close($curl);
        if ($ok === false) {
            throw new RuntimeException('Transport outcome unknown or response too large; preserve the original request');
        }
        if ($status < 200 || $status >= 300) {
            throw new RuntimeException('API HTTP '.$status.'; inspect the original operation');
        }

        return [$responseHeaders, $raw];
    }

    public static function accessToken(): string
    {
        if ($token = getenv('BINADIT_ACCESS_TOKEN')) {
            return $token;
        }
        $id = getenv('BINADIT_CLIENT_ID');
        $secret = getenv('BINADIT_CLIENT_SECRET');
        if (! $id || ! $secret) {
            throw new RuntimeException('Supply service credentials from your secret manager');
        }
        [, $raw] = self::send('POST', '/oauth/token', ['Accept: application/json', 'Content-Type: application/x-www-form-urlencoded'],
            http_build_query(['grant_type' => 'client_credentials', 'client_id' => $id, 'client_secret' => $secret]));
        $token = json_decode($raw, true, 512, JSON_THROW_ON_ERROR)['access_token'] ?? null;
        if (! is_string($token) || $token === '') {
            throw new RuntimeException('Token response is invalid');
        }

        return $token;
    }

    public static function api(string $token, string $method, string $path, ?array $body = null, ?string $version = null, ?string $key = null, bool $binary = false): array
    {
        if (! str_starts_with($path, '/api/v1/') || ! in_array($method, ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], true)) {
            throw new RuntimeException('Use a supported API method and path');
        }
        $headers = ['Authorization: Bearer '.$token, 'Accept: '.($binary ? 'application/pdf' : 'application/json')];
        if ($method !== 'GET') {
            if (! $key || ! preg_match('/\A[A-Za-z0-9_.:-]{16,128}\z/D', $key)) {
                throw new RuntimeException('Supply the persisted original Idempotency-Key');
            }
            $headers[] = 'Idempotency-Key: '.$key;
        }
        if ($version !== null) {
            $headers[] = 'If-Match: '.$version;
        }
        if ($body !== null) {
            $headers[] = 'Content-Type: application/json';
        }
        // Empty request objects are JSON {}, never the PHP empty-array encoding [].
        [$headers, $raw] = self::send($method, $path, $headers, $body === null ? null : json_encode($body === [] ? (object) [] : $body, JSON_THROW_ON_ERROR));
        if ($binary && explode(';', $headers['content-type'] ?? '')[0] !== 'application/pdf') {
            throw new RuntimeException('Expected an invoice PDF');
        }

        return ['etag' => $headers['etag'] ?? null, 'data' => $binary ? $raw : ($raw === '' ? null : json_decode($raw, true, 512, JSON_THROW_ON_ERROR))];
    }

    public static function verifyWebhook(string $raw, string $deliveryId, string $timestamp, string $signature, string $expectedKeyId, string $secret, ?int $now = null): bool
    {
        $uuid = '/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/D';
        if (strlen($raw) > 1024 * 1024 || strlen($secret) < 32 || ! preg_match($uuid, $deliveryId) || ! preg_match($uuid, $expectedKeyId)
            || ! preg_match('/\A[1-9][0-9]{8,10}\z/D', $timestamp) || abs(($now ?? time()) - (int) $timestamp) > 300) {
            return false;
        }
        $digest = hash_hmac('sha256', $timestamp.'.'.$deliveryId.'.'.$raw, $secret);

        return hash_equals('v1='.$digest.',key_id='.$expectedKeyId, $signature);
    }
}
```

## 2. Eenmalig voor deze workflow

```php
require 'binadit.php';
$token = BinaditExample::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`

```php
$result = BinaditExample::api($token, 'GET', '/api/v1/me');
```

## Bedrijven ophalen

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

```php
$result = BinaditExample::api($token, 'GET', '/api/v1/accounts');
```

## Beschikbaarheid en scopes controleren

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

```php
$result = BinaditExample::api($token, 'GET', '/api/v1/accounts/123/capabilities');
```

## Bedrijfsgegevens en ETag lezen

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

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::api($token, 'PATCH', '/api/v1/accounts/123/profile', ['city' => 'Utrecht', 'dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## Producten en prijzen bekijken

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

```php
$result = BinaditExample::api($token, 'GET', '/api/v1/accounts/123/products');
```

## Bestaande domeinhouders ophalen

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

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::api($token, 'POST', '/api/v1/accounts/123/holders/default', ['dry_run' => true], key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::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: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::api($token, 'POST', '/api/v1/accounts/123/domains/check', ['domains' => ['example.test']], key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::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: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## Een bewaarde offerte opnieuw lezen

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

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::api($token, 'POST', '/api/v1/accounts/123/orders', ['quote_id' => '11111111-1111-4111-8111-111111111111', 'dry_run' => true], key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## Registratie, verlenging en abonnementstatus lezen

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

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::api($token, 'POST', '/api/v1/accounts/123/domains/31/actions/cancel', ['confirm_domain' => 'example.test', 'dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## DNS-zones ophalen

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

```php
$result = BinaditExample::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`

```php
$result = BinaditExample::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`

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::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: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::api($token, 'POST', '/api/v1/accounts/123/dns/zones/8/template-plans', ['template_id' => 11, 'dry_run' => true], key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::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: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::api($token, 'POST', '/api/v1/accounts/123/dns/change-plans/11111111-1111-4111-8111-111111111111/apply', ['dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## Een oorspronkelijke operatie volgen

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

```php
$result = BinaditExample::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`

```php
$result = BinaditExample::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`

```php
$result = BinaditExample::api($token, 'GET', '/api/v1/accounts/123/billing/invoices/45/pdf', 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`

```php
$result = BinaditExample::api($token, 'GET', '/api/v1/accounts/123/workspaces');
```

## Mailboxen ophalen

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

```php
$result = BinaditExample::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`

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::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: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::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: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::api($token, 'PUT', '/api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24', ['dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::api($token, 'DELETE', '/api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24', ['dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::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: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::api($token, 'PUT', '/api/v1/accounts/123/workspaces/7/rooms/11/managers/24', ['dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::api($token, 'DELETE', '/api/v1/accounts/123/workspaces/7/rooms/11', ['confirm_address' => 'vergaderzaal@example.test', 'dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## Hostingabonnementen ophalen

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

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::api($token, 'POST', '/api/v1/accounts/123/hosting/subscriptions/41/actions/cancel', ['intent_id' => '33333333-3333-4333-8333-333333333333', 'dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## Spamfilters ophalen

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

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::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: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::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: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## Webhookafleveringen inspecteren

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

```php
$result = BinaditExample::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`

```php
$result = BinaditExample::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.

```php
$result = BinaditExample::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: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$payload = ['domain_id' => 12, 'product_id' => 9, 'local_part' => 'support', 'display_name' => 'Support', 'member_ids' => [23, 24], 'dry_run' => true];
$payload['product_version'] = getenv('BINADIT_PRODUCT_VERSION') ?: throw new RuntimeException('Read the product version first');
$result = BinaditExample::api($token, 'POST', '/api/v1/accounts/123/workspaces/7/shared-mailboxes', $payload, version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::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: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## 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.

```php
$result = BinaditExample::api($token, 'DELETE', '/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24', ['dry_run' => true], version: getenv('BINADIT_RESOURCE_VERSION') ?: throw new RuntimeException('Read the current ETag first'), key: getenv('BINADIT_REQUEST_KEY') ?: null);
```

## Verder werken

### Pagineren zonder resources over te slaan

```php
$cursor = null;
$seen = [];
for ($page = 0; $page < 20; $page++) {
    $query = ['limit' => 25];
    if ($cursor !== null) $query['cursor'] = $cursor;
    $result = BinaditExample::api($token, 'GET', '/api/v1/accounts/123/domains?'.http_build_query($query));
    foreach ($result['data']['data'] as $domain) echo $domain['id'].PHP_EOL;
    $cursor = $result['data']['meta']['next_cursor'] ?? null;
    if ($cursor === null) break;
    if (isset($seen[(string)$cursor])) throw new RuntimeException('Repeated pagination cursor');
    $seen[(string)$cursor] = true;
    if ($page === 19) throw new RuntimeException('Page limit reached; explicitly continue from the saved cursor');
}
```

Een lege `data`-lijst is geen bewijs dat de volgende pagina ontbreekt. Gebruik de cursor, geen willekeurige externe next-URL.

### PDF in een nieuw privébestand opslaan

```php
$result = BinaditExample::api($token, 'GET', '/api/v1/accounts/123/billing/invoices/45/pdf', binary: true);
$previousMask = umask(0077);
try { $file = fopen('factuur-45.pdf', 'xb'); } finally { umask($previousMask); }
if ($file === false) throw new RuntimeException('Choose a new private file');
try {
    $offset = 0;
    while ($offset < strlen($result['data'])) {
        $written = fwrite($file, substr($result['data'], $offset));
        if (!$written) throw new RuntimeException('PDF write failed');
        $offset += $written;
    }
} finally { fclose($file); }
```

Gebruik op Windows een map met passende privé-ACL. Bij een gedeeltelijke fout blijft het oorspronkelijke nieuwe bestand voor beoordeling staan; schrijf een volgend antwoord niet automatisch eroverheen.

### Webhookhandtekening controleren

```php
$raw = file_get_contents('php://input');  // Never re-encode parsed JSON for HMAC.
$valid = BinaditExample::verifyWebhook($raw, $deliveryIdHeader, $timestampHeader, $signatureHeader,
    getenv('BINADIT_WEBHOOK_KEY_ID') ?: '', getenv('BINADIT_WEBHOOK_SECRET') ?: '');
if (!$valid) { http_response_code(401); exit; }
// Next: atomically insert the delivery ID (UNIQUE) and enqueue work in your DB.
// Return 2xx only after that transaction commits; a valid durable duplicate gets 2xx too.
```

Lees en valideer de drie headers in je eigen framework, inclusief ontbrekende en dubbele headers, en stel een bodylimiet in. De functie controleert tijd en HMAC; de databaseconstraint voorkomt replay. Neem niet aan dat een geldige handtekening betekent dat het werk nog niet eerder is uitgevoerd.
