# init() parameters (/docs/sdk/init)



Call `tp.init()` once at startup, before your provider clients are imported. In practice you
set three things — the key, the collector URL, and the firewall mode. Everything else has a
sensible default.

```python
import token_police as tp
import os

tp.init(
    api_key=os.environ["TOKENPOLICE_API_KEY"],
    base_url="https://collect.tokenpolice.ai",
    firewall="dry_run",   # watch first; flip to "enforce" when ready
)
```

```typescript
import * as tp from "token-police";
import OpenAI from "openai";

tp.init({
  apiKey: process.env.TOKENPOLICE_API_KEY,
  baseUrl: "https://collect.tokenpolice.ai",
  firewall: "dry_run",
  instrumentModules: { openAI: OpenAI },   // Node: see Node & ESM
});
```

`https://collect.tokenpolice.ai` is also the built-in default in both SDKs, so you *can*
leave `base_url` out. Set it anyway — an explicit URL is what makes the destination visible
in your repo, and it's the one line you change to point at a self-hosted or local collector.

## The `firewall` mode [#the-firewall-mode]

This is the dial that decides whether TokenPolice can ever stop a call. It takes exactly
three values, spelled in lower case:

| `firewall`                        | Pre-call check | On a block decision         | What your app sees                   |
| --------------------------------- | -------------- | --------------------------- | ------------------------------------ |
| `"dry_run"&#x60; &#x2A;(default)* | Runs in full   | Recorded as a *would-block* | Nothing. The call proceeds.          |
| `"enforce"`                       | Runs in full   | Raised into your code       | `TokenPoliceBlockedError` — catch it |
| `"off"`                           | Skipped        | —                           | Nothing. No rule can act.            |

A few things worth knowing:

* **`"off"` is not "disabled".** Usage is still logged for every provider, so your dashboard
  and cost history keep filling up. Only the check and enforcement stop.
* **`"dry_run"` runs the *whole* check**, including the network round-trip — that's what makes
  its would-block records trustworthy. Budget for up to `timeout` of extra latency per call
  (0.5 s by default), and it fails open if the collector is slow.
* **The value is matched byte for byte.** `"Enforce"` or `"DRY_RUN"` is not recognised: the SDK
  falls back to `"dry_run"` — never to `"enforce"` — so a typo can never start blocking traffic
  by accident. Node prints a warning; Python only prints it when `log_errors=True`.
* **A reroute is enforcement too.** A rule that swaps the model only takes effect under
  `"enforce"`; in dry-run it is recorded and nothing changes.

<Callout type="warn">
  **Two dials, not one.** Setting `firewall="enforce"` is only half of it. Each Block or
  Reroute rule you create also has its own Enforce / Dry-run setting in the dashboard, and a
  call is blocked only when **both** say enforce. See
  [Dry-run vs enforce](/docs/concepts/dry-run-vs-enforce).
</Callout>

<Callout type="info">
  **The one exception to fail-open.** If TokenPolice becomes unreachable, calls go through —
  with one deliberate exception: under `"enforce"` on a **daemon** deployment, something the
  firewall had *already* blocked stays blocked through the outage. An outage can't be used to
  lift a limit you set. The exception needs the SDK's open update stream, which it opens only
  when `deployment` is `"daemon"`; on `"serverless"` and `"edge"` there is no stream and an
  outage fails fully open. See [Fail-open](/docs/concepts/fail-open).
</Callout>

## Python parameters [#python-parameters]

| Parameter                            | Type          | Default                                                           | What it does                                                                                                                                                                                                                             |
| ------------------------------------ | ------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `api_key`                            | `str`         | env `TOKENPOLICE_API_KEY`, else raises                            | Your `tp_sk_…` key. A key that doesn't start with `tp_sk_` only warns.                                                                                                                                                                   |
| `base_url`                           | `str`         | env `TOKENPOLICE_BASE_URL`, else `https://collect.tokenpolice.ai` | Collector URL. Trailing slashes are stripped.                                                                                                                                                                                            |
| `firewall`                           | `str`         | `"dry_run"`                                                       | `"dry_run"` / `"enforce"` / `"off"` — see above.                                                                                                                                                                                         |
| `timeout`                            | `float`       | `2.0`                                                             | Max **seconds** the SDK waits for TokenPolice before letting the call through anyway. Python takes the value as given — it isn't validated or clamped (Node's is).                                                                       |
| `log_errors`                         | `bool`        | `False`                                                           | Turn on the SDK's own diagnostic output. An integration aid — turn it back off when you're done.                                                                                                                                         |
| `capture_stream_usage`               | `bool`        | on                                                                | Makes streamed calls report token counts. Off, a streamed call's cost is lost. `TP_CAPTURE_STREAM_USAGE=0` disables it.                                                                                                                  |
| `error_detail`                       | `str`         | `"redacted"`                                                      | How much of a *failed* provider call's error text leaves your process: `"none"` (classifier only), `"redacted"` (exception class + a hash of the message), `"raw"` (the verbatim, truncated string). Unknown values become `"redacted"`. |
| `deployment`                         | `str`         | `"auto"`                                                          | `"auto"` / `"daemon"` / `"serverless"` / `"edge"`. Auto-detection is usually right, and setting it does **not** flush for you — see [Flushing & serverless](/docs/sdk/flushing-and-serverless).                                          |
| `max_workers`                        | `int`         | CPU-based                                                         | **Python only.** Size of the background pool that ships usage logs off your request path.                                                                                                                                                |
| `tracer_provider`                    | OTel provider | `None`                                                            | **Python only.** Attach TokenPolice to an OpenTelemetry provider you already run instead of the private one it builds.                                                                                                                   |
| `sse_reconnect_max_interval_seconds` | `int`         | `300`                                                             | Cap on the backoff between attempts to re-establish the rule feed. Rarely set.                                                                                                                                                           |
| `stream_stale_grace_seconds`         | `float`       | `60`                                                              | Clamped to 0–3600. How long the SDK keeps trusting its cached rule view after the feed drops, before re-checking a matching call with the server. `0` re-checks from the first missed moment.                                            |
| `enforce`                            | `bool`        | —                                                                 | Deprecated alias — don't use it. Use `firewall`.                                                                                                                                                                                         |

## Node parameters [#node-parameters]

Same options, camelCased.

| Option                           | Type                | Default                                                           | What it does                                                                                                       |
| -------------------------------- | ------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `apiKey`                         | `string`            | env `TOKENPOLICE_API_KEY`, else throws                            | Your `tp_sk_…` key. A key that doesn't start with `tp_sk_` only warns.                                             |
| `baseUrl`                        | `string`            | env `TOKENPOLICE_BASE_URL`, else `https://collect.tokenpolice.ai` | Collector URL. Trailing slashes are stripped.                                                                      |
| `firewall`                       | `FirewallMode`      | `"dry_run"`                                                       | Same three values, same byte-exact matching, same safe fallback.                                                   |
| `timeout`                        | `number`            | `2.0`                                                             | Max **seconds**. A non-positive or non-finite value warns and falls back to `2.0`.                                 |
| `logErrors`                      | `boolean`           | `false`                                                           | Same as Python; also un-gates the SDK's debug lines.                                                               |
| `captureStreamUsage`             | `boolean`           | on                                                                | Same as Python; `TP_CAPTURE_STREAM_USAGE=0` disables.                                                              |
| `errorDetail`                    | `ErrorDetailMode`   | `"redacted"`                                                      | Same three modes and the same safe coercion as Python.                                                             |
| `deployment`                     | `string`            | `"auto"`                                                          | Same as Python, including edge runtimes.                                                                           |
| `instrumentModules`              | `InstrumentModules` | —                                                                 | **Node only.** Hand the SDK the provider modules your app already imported — see [Node & ESM](/docs/sdk/node-esm). |
| `sseReconnectMaxIntervalSeconds` | `number`            | `300`                                                             | Same as Python.                                                                                                    |
| `streamStaleGraceSeconds`        | `number`            | `60`                                                              | Same as Python, same 0–3600 clamp.                                                                                 |
| `enforce`                        | `boolean`           | —                                                                 | Deprecated alias — don't use it. Use `firewall`.                                                                   |

Node has no `maxWorkers` or `tracerProvider` (it's `fetch`-based, with no thread pool).

<Callout type="info">
  Call `init()` once per process. Calling it again replaces the client and says so in the
  log — and after [`uninstrument()`](/docs/sdk/uninstrument) a second `init()` can't re-attach
  at all. Other than a missing API key, `init()` never throws: if anything inside it fails, the
  SDK degrades instead of taking your app down.
</Callout>

## Environment variables [#environment-variables]

Three, and three only:

| Variable                  | Replaces                                                     |
| ------------------------- | ------------------------------------------------------------ |
| `TOKENPOLICE_API_KEY`     | `api_key` / `apiKey`                                         |
| `TOKENPOLICE_BASE_URL`    | `base_url` / `baseUrl`                                       |
| `TP_CAPTURE_STREAM_USAGE` | `capture_stream_usage` / `captureStreamUsage` (`0` disables) |

<Callout type="warn">
  The prefix is `TOKENPOLICE_`, one word. `TOKEN_POLICE_API_KEY` (with an underscore) is read by
  nothing — a common copy-paste trap.
</Callout>

## Next [#next]

<Cards>
  <Card title="Dry-run vs enforce" href="/docs/concepts/dry-run-vs-enforce" description="The two dials that decide whether a block ever fires." />

  <Card title="Supported versions" href="/docs/sdk/supported-versions" description="Runtimes, providers, and the versions we've verified." />

  <Card title="Wrapping your app" href="/docs/sdk/wrapping" description="Tag your calls with identity and structure." />
</Cards>
