The client needs three things. Each one comes from the constructor or, if you leave it out, from an environment variable.
| Setting | TypeScript option | Python argument | Environment | Default |
|---|---|---|---|---|
| API token | token | token= | OPENWAF_TOKEN | none: calls fail with no_token |
| API address | baseUrl | base_url= | OPENWAF_API_URL | a built-in address that does not serve the API: always set this |
| Organisation id | org | org= | OPENWAF_ORG | the token's organisation, asked once with GET /v1/orgs |
Set the API address to https://dash.openwaf.dev; the SDK adds the /v1/… paths.
import { OpenWAF } from "./openwaf.ts";
// From the environment: OPENWAF_API_URL, OPENWAF_TOKEN, OPENWAF_ORG.
const owf = new OpenWAF();
// Or explicitly, for example in a browser extension or a worker without process.env.
const other = new OpenWAF({ baseUrl: "https://dash.openwaf.dev", token: myToken });The TypeScript client reads the environment in Node.js, Bun and Deno. Elsewhere, pass the options. You can also pass your own fetch implementation in fetch.
from openwaf import OpenWAF
# From the environment: OPENWAF_API_URL, OPENWAF_TOKEN, OPENWAF_ORG.
owf = OpenWAF()
# Or explicitly, with a longer timeout (seconds, default 60).
other = OpenWAF(base_url="https://dash.openwaf.dev", token=my_token, timeout=120)The token
Create tokens in the dashboard under API tokens. A token has a role (Read only, Tech, Admin) and a scope (the whole organisation, one client or one zone); the API enforces both on every call, whatever SDK method you use. Give each script the narrowest token that works, and keep it in a secret store or an environment variable, never in code.
The client sends it as Authorization: Bearer owf_… on every call except the public ones (the origin TLS CA bundles).
The organisation
Every method under /v1/orgs/{org} takes an optional org. Without it, the client uses its own: the org you configured, or the token's organisation, which it asks the API for once and then remembers. Call orgId() (TypeScript) or org_id() (Python) to get it.
Zones are named by id
Zone methods take the zone's id, not its domain. Look it up once with listZones / list_zones:
const { items = [] } = await owf.listZones();
const zone = items.find((z) => z.name === "example.com");zone = next(z for z in owf.list_zones()["items"] if z["name"] == "example.com")