# Binadit API met Python

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

Python 3.11+, alleen de standaardbibliotheek.

## 1. Herbruikbare client

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

```python
"""Python 3.11+, standard library only. No network calls when imported."""
import json
import hashlib
import hmac
import os
import re
import time
import urllib.error
import urllib.parse
import urllib.request

ORIGIN = "https://my.binadit.com"
LIMIT = 8 * 1024 * 1024


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None


def send(method, path, headers, content=None):
    if not (path.startswith("/api/v1/") or path == "/oauth/token") or "\\" in path or "#" in path:
        raise ValueError("Use a relative Binadit API path")
    request = urllib.request.Request(ORIGIN + path, content, headers, method=method)
    try:
        response = urllib.request.build_opener(NoRedirect()).open(request, timeout=30)
    except urllib.error.HTTPError as error:
        response = error
    except (OSError, urllib.error.URLError):
        raise RuntimeError("Transport outcome unknown; preserve the original request") from None
    with response:
        raw = response.read(LIMIT + 1)
        if len(raw) > LIMIT:
            raise RuntimeError("Response exceeds the example's 8 MiB limit")
        if not 200 <= response.status < 300:
            raise RuntimeError(f"API HTTP {response.status}; inspect the original operation")
        return response.headers, raw


def access_token():
    token = os.environ.get("BINADIT_ACCESS_TOKEN")
    if token:
        return token
    body = urllib.parse.urlencode({"grant_type": "client_credentials",
        "client_id": os.environ["BINADIT_CLIENT_ID"],
        "client_secret": os.environ["BINADIT_CLIENT_SECRET"]}).encode()
    _, raw = send("POST", "/oauth/token", {"Accept": "application/json",
        "Content-Type": "application/x-www-form-urlencoded"}, body)
    token = json.loads(raw)["access_token"]
    if not isinstance(token, str) or not token:
        raise RuntimeError("Token response is invalid")
    return token


def api(token, method, path, body=None, *, version=None, key=None, binary=False):
    if not path.startswith("/api/v1/") or method not in {"GET", "POST", "PUT", "PATCH", "DELETE"}:
        raise ValueError("Use a supported API method and path")
    headers = {"Authorization": f"Bearer {token}", "Accept": "application/pdf" if binary else "application/json"}
    if method != "GET":
        if not key or not re.fullmatch(r"[A-Za-z0-9_.:-]{16,128}", key):
            raise ValueError("Supply the persisted original Idempotency-Key")
        headers["Idempotency-Key"] = key
    if version:
        headers["If-Match"] = version
    encoded = None if body is None else json.dumps(body, ensure_ascii=False).encode()
    if encoded is not None:
        headers["Content-Type"] = "application/json"
    response, raw = send(method, path, headers, encoded)
    if binary and response.get_content_type() != "application/pdf":
        raise RuntimeError("Expected an invoice PDF")
    return {"etag": response.get("ETag"), "data": raw if binary else (json.loads(raw) if raw else None)}


def verify_webhook(raw, delivery_id, timestamp, signature, expected_key_id, secret, now=None):
    """Signature/time check only; separately deduplicate in a durable transaction."""
    uuid = r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
    if (len(raw) > 1024 * 1024 or len(secret) < 32
            or not re.fullmatch(uuid, delivery_id) or not re.fullmatch(uuid, expected_key_id)
            or not re.fullmatch(r"[1-9][0-9]{8,10}", timestamp)):
        return False
    if abs((time.time() if now is None else now) - int(timestamp)) > 300:
        return False
    digest = hmac.new(secret.encode(), timestamp.encode() + b"." + delivery_id.encode() + b"." + raw, hashlib.sha256).hexdigest()
    expected = f"v1={digest},key_id={expected_key_id}"
    return signature.isascii() and hmac.compare_digest(expected, signature)
```

## 2. Eenmalig voor deze workflow

```python
from binadit import access_token, api
import os
token = access_token()
```

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`

```python
result = api(token, 'GET', '/api/v1/me')
```

## Bedrijven ophalen

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

```python
result = api(token, 'GET', '/api/v1/accounts')
```

## Beschikbaarheid en scopes controleren

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

```python
result = api(token, 'GET', '/api/v1/accounts/123/capabilities')
```

## Bedrijfsgegevens en ETag lezen

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

```python
result = 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.

```python
result = api(token, 'PATCH', '/api/v1/accounts/123/profile', {'city': 'Utrecht', 'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["BINADIT_REQUEST_KEY"])
```

## Producten en prijzen bekijken

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

```python
result = api(token, 'GET', '/api/v1/accounts/123/products')
```

## Bestaande domeinhouders ophalen

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

```python
result = 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.

```python
result = api(token, 'POST', '/api/v1/accounts/123/holders/default', {'dry_run': True}, key=os.environ["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.

```python
result = 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=os.environ["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.

```python
result = api(token, 'POST', '/api/v1/accounts/123/domains/check', {'domains': ['example.test']}, key=os.environ["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.

```python
result = 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=os.environ["BINADIT_REQUEST_KEY"])
```

## Een bewaarde offerte opnieuw lezen

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

```python
result = 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.

```python
result = api(token, 'POST', '/api/v1/accounts/123/orders', {'quote_id': '11111111-1111-4111-8111-111111111111', 'dry_run': True}, key=os.environ["BINADIT_REQUEST_KEY"])
```

## Registratie, verlenging en abonnementstatus lezen

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

```python
result = 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.

```python
result = api(token, 'POST', '/api/v1/accounts/123/domains/31/actions/cancel', {'confirm_domain': 'example.test', 'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["BINADIT_REQUEST_KEY"])
```

## DNS-zones ophalen

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

```python
result = 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`

```python
result = 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`

```python
result = 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.

```python
result = 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=os.environ["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.

```python
result = api(token, 'POST', '/api/v1/accounts/123/dns/zones/8/template-plans', {'template_id': 11, 'dry_run': True}, key=os.environ["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.

```python
result = 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=os.environ["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.

```python
result = api(token, 'POST', '/api/v1/accounts/123/dns/change-plans/11111111-1111-4111-8111-111111111111/apply', {'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["BINADIT_REQUEST_KEY"])
```

## Een oorspronkelijke operatie volgen

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

```python
result = 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`

```python
result = 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`

```python
result = 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`

```python
result = api(token, 'GET', '/api/v1/accounts/123/workspaces')
```

## Mailboxen ophalen

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

```python
result = 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`

```python
result = 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.

```python
result = 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=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["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.

```python
result = 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=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["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.

```python
result = api(token, 'PUT', '/api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24', {'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["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.

```python
result = api(token, 'DELETE', '/api/v1/accounts/123/workspaces/7/mailing-lists/10/members/mailbox-24', {'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["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.

```python
result = 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=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["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.

```python
result = api(token, 'PUT', '/api/v1/accounts/123/workspaces/7/rooms/11/managers/24', {'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["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.

```python
result = api(token, 'DELETE', '/api/v1/accounts/123/workspaces/7/rooms/11', {'confirm_address': 'vergaderzaal@example.test', 'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["BINADIT_REQUEST_KEY"])
```

## Hostingabonnementen ophalen

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

```python
result = 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.

```python
result = api(token, 'POST', '/api/v1/accounts/123/hosting/subscriptions/41/actions/cancel', {'intent_id': '33333333-3333-4333-8333-333333333333', 'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["BINADIT_REQUEST_KEY"])
```

## Spamfilters ophalen

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

```python
result = 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.

```python
result = 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=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["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.

```python
result = 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=os.environ["BINADIT_REQUEST_KEY"])
```

## Webhookafleveringen inspecteren

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

```python
result = 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`

```python
result = 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.

```python
result = 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=os.environ["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.

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

```python
result = 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=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["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.

```python
result = api(token, 'DELETE', '/api/v1/accounts/123/workspaces/7/mailboxes/23/delegates/24', {'dry_run': True}, version=os.environ["BINADIT_RESOURCE_VERSION"], key=os.environ["BINADIT_REQUEST_KEY"])
```

## Verder werken

### Pagineren zonder resources over te slaan

Lees `meta.next_cursor`, ook wanneer de gefilterde `data`-lijst leeg is. Volg geen willekeurige `next`-URL met je Bearer-token. Onderstaande lus stopt met een fout als de begrenzing wordt bereikt.

```python
import urllib.parse
cursor = None
seen = set()
for page in range(20):
    path = "/api/v1/accounts/123/domains?limit=25"
    if cursor is not None:
        path += "&cursor=" + urllib.parse.quote(str(cursor), safe="")
    result = api(token, "GET", path)
    for domain in result["data"]["data"]:
        print(domain["id"])  # Only the chosen safe field.
    cursor = result["data"].get("meta", {}).get("next_cursor")
    if cursor is None:
        break
    if str(cursor) in seen:
        raise RuntimeError("Repeated pagination cursor")
    seen.add(str(cursor))
else:
    raise RuntimeError("Page limit reached; explicitly continue from the saved cursor")
```

### PDF opslaan zonder bestaand bestand te overschrijven

```python
import os
result = api(token, "GET", "/api/v1/accounts/123/billing/invoices/45/pdf", binary=True)
fd = os.open("factuur-45.pdf", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "wb") as output:
    output.write(result["data"])
```

Gebruik op Windows een map waarvan alleen de bedoelde gebruiker de ACL-toegang heeft. Een fout is geen toestemming om een bestaand bestand alsnog te overschrijven.

### Webhookhandtekening controleren

```python
from binadit import verify_webhook
# Obtain raw bytes and the headers from your server framework before parsing JSON.
valid = verify_webhook(raw_body, delivery_id_header, timestamp_header, signature_header,
                       os.environ["BINADIT_WEBHOOK_KEY_ID"], os.environ["BINADIT_WEBHOOK_SECRET"])
if not valid:
    raise ValueError("Reject this webhook")
# Next: atomically insert the delivery ID (UNIQUE) and enqueue the work in your DB.
```

Configureer een passende bodylimiet en controleer ontbrekende of dubbele headers in je framework. De functie weigert meer dan 1 MiB, een onjuist tijdvenster, onbekende sleutel-ID en foutieve HMAC. Zij voorkomt zelf geen replay: voeg de unieke delivery-ID en het werkitem in dezelfde duurzame transactie toe voordat je 2xx antwoordt. Lees [de afspraken voor alle talen](/developers/guides/code-examples?lang=nl).
