# Python SDK

> The official Python client — standard library only, typed dicts, automatic retries and idempotency, auto-pagination and webhook verification. Python 3.9+.

## Install {#install}

```bash
pip install checkbox-site
```

> **Pre-release packages**
>
> The SDKs, the CLI and the local MCP server are not on npm or PyPI yet, and the package names shown here are
> placeholders (the CLI commands below run from a checkout of the repository today). Until this notice is gone, use the
> HTTP API (curl) or the remote MCP server at `https://app.checkbox.site/mcp`, which are live today. Never install an npm
> package named `checkbox`: it belongs to an unrelated project.

```python
import os
from checkbox import Checkbox

checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])

account = checkbox.account.retrieve()
print(checkbox.mode, account["activation"]["status"])
```

`Checkbox()` without an argument reads `CHECKBOX_API_KEY`. Keyword options: `base_url` (or `CHECKBOX_API_BASE`),
`api_version`, `max_network_retries` (default 2), `timeout` (seconds, default 80), `app_info`, `headers`, `transport`.

## Methods {#methods}

Every operation is a method: `checkbox.<resource>.<action>()` in snake_case (`createSite` → `checkbox.sites.create()`,
`checkStripeCheckoutSession` → `checkbox.checks.stripe_checkout_session()`). Path parameters are positional;
parameters are keyword arguments (or one dict). Responses are dicts; `checkbox.types` has their `TypedDict` shapes.

```python
import os
from checkbox import Checkbox

checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])

site = checkbox.call("createSite", {"url": "https://joybox.example"})
plan = checkbox.call("retrieveCompliancePlan", {"site": site["id"]})
print(plan["status"], plan["next_action"]["type"])
```

## Pagination {#pagination}

```python
import os
from checkbox import Checkbox

checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])

page = checkbox.sites.list(limit=10)
print(len(page.data), page.has_more)

for finding in checkbox.findings.list(status="fail", limit=100).auto_paging_iter():
    print(finding["check_id"], finding["severity"])
```

## Errors {#errors}

```python
import os
from checkbox import Checkbox, InvalidRequestError

checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])

try:
    checkbox.sites.retrieve("site_00000000000000000000000000000000")
except InvalidRequestError as err:
    if err.code != "resource_missing":
        raise
    print("No such site:", err.code, err.status_code)
```

Error classes: `InvalidRequestError`, `AuthenticationError`, `PermissionError`, `RateLimitError`, `IdempotencyError`,
`APIError`, `APIConnectionError`, `SignatureVerificationError` — all subclasses of `CheckboxError`, with `code`,
`param`, `status_code`, `request_id` and `next_action`.

## Retries and idempotency {#retries}

Network errors, `429` and `5xx` are retried with exponential backoff, and every POST carries an `Idempotency-Key` that
is reused across retries. Pass your own in `request_options`:

```python
import os
from checkbox import Checkbox

checkbox = Checkbox(os.environ["CHECKBOX_SECRET_KEY"])

site = checkbox.sites.create(url="https://joybox.example", request_options={"idempotency_key": "site-joybox-example"})
print(site["id"])
```

## Webhooks {#webhooks}

```python
import json
from checkbox import webhook

secret = "whsec_test_secret"
payload = json.dumps({"id": "evt_123", "object": "event", "type": "site.compliant", "data": {"object": {"id": "site_123"}}})
header = webhook.generate_test_header(payload, secret)  # what Checkbox sends as Checkbox-Signature

event = webhook.construct_event(payload, header, secret)
print(event["type"])
```

Pass the **raw** request body (bytes or str) exactly as received. `construct_event` raises
`SignatureVerificationError` for a wrong or stale (over 5 minutes) signature. See [Webhooks](https://checkbox.site/docs/webhooks).
