# SDK configuration reference

> Every ApiTraffic Node SDK option and environment variable, its default and effect, how code, environment and account settings combine, and the per-request APIs.

Source: https://www.apitraffic.io/docs/integrations/configuration

This page lists every setting the ApiTraffic Node SDK reads, with its type, default and effect, and the functions you can call from your request-handling code. It applies to `@apitraffic/node` and to the per-framework packages (`@apitraffic/express`, `@apitraffic/fastify`, `@apitraffic/koa`, `@apitraffic/hapi`), which accept the same options. The Strapi plugin takes them through its plugin configuration; see [Strapi](https://www.apitraffic.io/docs/integrations/strapi#configure-the-plugin).

Most applications need only two values, `API_TRAFFIC_TOKEN` and `API_TRAFFIC_BUCKET`. Everything that decides what is captured, redacted or blocked is set in your ApiTraffic account and delivered to the SDK, not configured here.

## Where settings come from

The SDK combines three sources:

1. **Options you pass in code**, to `apitraffic.express({...})`, `apitraffic.fastify({...})` and the other framework calls.
2. **`API_TRAFFIC_*` environment variables**, read when the SDK starts.
3. **Settings delivered from your ApiTraffic account**: redaction rules, exclusions, the firewall policy, APM capture switches and host telemetry. The SDK loads them on startup and receives changes in real time, without a restart.

### Precedence

For every option in the tables below: **an option passed in code wins over its environment variable, which wins over the built-in default.** You can mix the two, for example the token from the environment and `debug` in code.

A few settings follow a different rule, and each is called out in its table:

| Setting | Rule |
|---|---|
| `offlineWafMode` | The environment variable `API_TRAFFIC_OFFLINE_WAF_MODE` wins over the code option, so you can change it during an incident without a deploy. |
| Firewall on or off | Off if **either** `waf: false` is passed or `API_TRAFFIC_WAF_ENABLED=false` is set. Neither can turn on a firewall your account has not enabled. |
| `captureStatementParams`, `captureRedisKeys` | On if **either** your code or environment turns it on, **or** the bucket's **APM Capture** settings do. The dashboard can add capture, never remove what your code asked for. |
| `captureCallSite`, `captureStackDepth` | The bucket's **APM Capture** setting applies, unless you set the option in code or the environment, which then wins. |
| `interceptOutbound` | A blank value (`""` or `null`, or an empty environment variable) turns outbound capture **off**. For every other option, a blank value counts as not set. |

### How values are parsed

Options that switch something on or off, and numeric limits, accept strings as well as booleans and numbers, so `{ captureSpans: process.env.CAPTURE_SPANS }` works:

- **Switches** accept `true`/`false`, `yes`/`no`, `on`/`off` or `1`/`0`, in any case.
- **Byte caps and counts** must be whole numbers greater than 0. `captureStackDepth` may be 0.
- **Sample rates** are numbers from `0` to `1`.
- **A blank value** (`""`, spaces, or `null`) counts as not provided, so the environment variable, then the default, applies. A blank never means 0 or off (except for `interceptOutbound`).
- **An invalid value** (`"abc"`, `-5`, `1.5` for a rate) is reported once on the console and ignored, and the next source applies.

> **Warning:** `debug` and `strict` are not parsed from strings in code: pass `true` or `false`. `API_TRAFFIC_DEBUG` turns debug logging on only when it is exactly `true` (any case); `1` or `yes` do not.

## Credentials and connection

| Option | Environment variable | Type | Default | Effect |
|---|---|---|---|---|
| `token` | `API_TRAFFIC_TOKEN` | string | - | **Required.** Your ingest token. Without it the SDK logs an error and disables telemetry for the process; your app still starts. |
| `bucket` | `API_TRAFFIC_BUCKET` | string | - | **Required.** The ID of the bucket captured traffic is sent to: seven lowercase letters and digits, such as `k3m9p2x`. The setup page and **Bucket Settings** show it. |
| `accountSid` | `API_TRAFFIC_ACCOUNT_SID` | string | Learned from your account settings | Only needed by a process that mints [embed tokens](https://www.apitraffic.io/docs/integrations/embed) without running the ApiTraffic middleware. |
| - | `API_TRAFFIC_EMBED_TOKEN` | string | - | An **Administrator** [API token](https://www.apitraffic.io/docs/account/api-tokens) that `embedToken()` mints [embed tokens](https://www.apitraffic.io/docs/integrations/embed) with. The ingest token in `API_TRAFFIC_TOKEN` can't mint them. A `token` passed to `embedToken()` takes precedence. It is a read credential to your captured traffic: set it only on servers that mint embed tokens, and keep it secret. |
| `apiHost` | `API_TRAFFIC_API_HOST` | string | `api.apitraffic.io` | Host the SDK loads its settings from. A bare host name means `https://`. |
| `ingestHost` | `API_TRAFFIC_INGEST_HOST` | string | `ingest.apitraffic.io` | Host captured traffic is sent to. A bare host name means `https://`. |
| `debug` | `API_TRAFFIC_DEBUG` | boolean | `false` | Log SDK activity to the console. Useful while installing; leave it off in production. |
| `strict` | - | boolean | `false` | Throw at startup on misconfiguration (missing token or bucket) instead of logging and disabling telemetry. For apps that would rather not start than run unmonitored. |

The SDK warns once if `apiHost` or `ingestHost` is plain `http://` and not this machine, because the token and captured requests would cross the network unencrypted.

**Proxies.** Delivery to ApiTraffic honors the standard `HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY` and `NO_PROXY` environment variables (in upper or lower case).

## Capture

| Option | Environment variable | Type | Default | Effect |
|---|---|---|---|---|
| `interceptOutbound` | `API_TRAFFIC_INTERCEPT_OUTBOUND` | switch | `true` | Capture outbound calls made with `http`, `https` and `fetch`. Off leaves those modules untouched. See [Outbound requests](https://www.apitraffic.io/docs/outbound-requests). |
| `captureFetch` | `API_TRAFFIC_CAPTURE_FETCH` | switch | `true` | Capture outbound global `fetch` calls (requires `interceptOutbound`). Off affects telemetry only: an [egress firewall policy](https://www.apitraffic.io/docs/waf/policies) still applies to `fetch`. |
| `redactSensitiveKeys` | `API_TRAFFIC_REDACT_SENSITIVE_KEYS` | switch | `true` | Mask credential-shaped body keys and query parameters (`password`, `token`, `secret`, `api_key` and similar) in addition to your redaction rules. Credential headers are always masked. |
| `maxBodyBytes` | `API_TRAFFIC_MAX_BODY_BYTES` | bytes | `524288` (512 KB) | Largest request or response body sent to ApiTraffic. A larger body is replaced by a placeholder. Applied after redaction. |
| `maxRedactBodyBytes` | `API_TRAFFIC_MAX_REDACT_BODY_BYTES` | bytes | `1048576` (1 MB) | Largest body the SDK redacts. A larger body is replaced by a placeholder before any redaction work, so redaction cost stays bounded. |
| `maxFetchBodyLength` | `API_TRAFFIC_MAX_FETCH_BODY_LENGTH` | bytes | `524288` (512 KB) | Largest outbound request or response body captured, for `http`, `https` and `fetch` calls. |
| `requestSampleRate` | `API_TRAFFIC_REQUEST_SAMPLE_RATE` | rate | `1` | Fraction of captured requests sent. Requests with a `5xx` response status are always sent, so `0` (only when given as `0` or `"0"`) sends server errors only. |

Bodies that cannot be captured safely - nested more than 256 levels deep, unreadable, or not processable by your redaction rules - are replaced with a `[body not captured: …]` placeholder and the rest of the request is still sent. A request whose redaction cannot be completed at all is dropped, never sent unredacted.

## Delivery

| Option | Environment variable | Type | Default | Effect |
|---|---|---|---|---|
| `ingestBatch` | `API_TRAFFIC_INGEST_BATCH` | switch | `true` | Send several payloads per request to ApiTraffic when your account and the ingest host both support it. `false` sends one request per payload. |
| `ingestGzip` | `API_TRAFFIC_INGEST_GZIP` | switch | `true` | Compress those batches when your account asks for it. |

Delivery limits (timeouts, queue size, circuit breaker) are fixed; they are described in [If ApiTraffic is unreachable](https://www.apitraffic.io/docs/reference/unreachable#telemetry-delivery).

## Spans and APM

These options control the spans recorded inside each request: database, cache, queue and outbound work from the [APM bridge](https://www.apitraffic.io/docs/apm/setup), and your own spans from [`span()` and `startSpan()`](#time-your-own-work). Set them on the framework call or with their environment variables.

| Option | Environment variable | Type | Default | Effect |
|---|---|---|---|---|
| `captureSpans` | `API_TRAFFIC_CAPTURE_SPANS` | switch | `true` | Master switch for spans and per-request span rollups. |
| `captureStatements` | `API_TRAFFIC_CAPTURE_STATEMENTS` | switch | `true` | Include the normalized SQL or command text (`where id = ?`) in spans. |
| `captureStatementParams` | `API_TRAFFIC_CAPTURE_STATEMENT_PARAMS` | switch | `false` | Also keep statements as issued, with their bind values. These carry your users' data. Combines with the bucket's **Query Parameters** setting: either one turns it on. |
| `captureRedisKeys` | `API_TRAFFIC_CAPTURE_REDIS_KEYS` | switch | `false` | Include the concrete Redis key, unmasked and not run through redaction rules. Combines with the bucket's **Redis Keys** setting: either one turns it on. |
| `captureCallSite` | `API_TRAFFIC_CAPTURE_CALL_SITE` | switch | `true` (bucket setting) | Record the application code location (`file:line`) that started each span. Unset follows the bucket's **Code Location** setting; set, it wins. |
| `captureStackDepth` | `API_TRAFFIC_CAPTURE_STACK_DEPTH` | whole number, 0 or more | `10` (bucket setting) | Application frames kept per span; `1` keeps the call site alone. Unset follows the bucket. |
| `projectRoot` | `API_TRAFFIC_PROJECT_ROOT` | string | Detected | Directory code locations are reported relative to. Detected from your workspace file, `.git` or `package.json`; set it when a bundled or serverless build has removed those. |
| `redisKeyPatterns` | - | array of strings or RegExps | `[]` | Patterns for Redis keys whose variable parts the automatic masking cannot infer. |
| `maxSpansPerRequest` | `API_TRAFFIC_MAX_SPANS_PER_REQUEST` | count | `500` | Spans kept per request. Work past the cap is still counted in the request's rollups. |
| `spanSampleRate` | `API_TRAFFIC_SPAN_SAMPLE_RATE` | rate | `1` | Fraction of requests that keep full span detail. Rollups are computed for every request. |
| `maxSpanMetaBytes` | `API_TRAFFIC_MAX_SPAN_META_BYTES` | bytes | `2048` | Size cap on each span's metadata, after redaction. |
| `sendStatementTextOnce` | `API_TRAFFIC_SEND_STATEMENT_TEXT_ONCE` | switch | `false` | Send each query's statement text and call stack once per process per resend window, instead of on every span, to reduce payload size. |
| `statementTextResendSeconds` | `API_TRAFFIC_STATEMENT_TEXT_RESEND_SECONDS` | count | `3600` | How long text already sent is left off before it is sent again. |
| `statementTextCacheSize` | `API_TRAFFIC_STATEMENT_TEXT_CACHE_SIZE` | count | `5000` | Queries remembered per process for `sendStatementTextOnce`. |

### Options for the APM preload

`register()` (and `node -r @apitraffic/node/register`, which calls it) takes its own options, from the call or from these environment variables. The switches above that it shares (`captureSpans`, `captureStatements`, `captureStatementParams`, `captureRedisKeys`, `captureCallSite`, `captureStackDepth`, `maxSpanMetaBytes`, `debug`) read the same variables.

| Option | Environment variable | Type | Default | Effect |
|---|---|---|---|---|
| `enabledInstrumentations` | `API_TRAFFIC_ENABLED_INSTRUMENTATIONS` | list | `[]` | Instrumentations to turn on beyond the default set, by full name (`@opentelemetry/instrumentation-graphql`) or short name (`graphql`). Comma-separated in the variable. |
| `disabledInstrumentations` | `API_TRAFFIC_DISABLED_INSTRUMENTATIONS` | list | `[]` | Instrumentations to turn off. Wins over everything else. |
| `instrumentHttp` | `API_TRAFFIC_INSTRUMENT_HTTP` | switch | `false` | Enable OpenTelemetry's `http`/`https` client instrumentation. Off because the SDK already records those calls with bodies. |
| `synthesizeOutboundRecords` | `API_TRAFFIC_SYNTHESIZE_OUTBOUND_RECORDS` | switch | `true` | Record outbound calls the SDK cannot see itself, such as `undici.request` used directly, from their spans (metadata only). |
| `flushTimeoutMs` | `API_TRAFFIC_APM_FLUSH_TIMEOUT_MS` | milliseconds | `5000` | Longest the span processor's flush or shutdown waits for queued payloads to be delivered. |
| `serviceName` | `API_TRAFFIC_SERVICE_NAME` | string | - | Service name reported to OpenTelemetry. |
| `force` | `API_TRAFFIC_APM_FORCE` | switch | `false` | Start even though Datadog or New Relic appears to be active. Never overrides `OTEL_SDK_DISABLED` or an OpenTelemetry provider already registered. |

What the default instrumentation set covers, and how to use APM with an OpenTelemetry setup of your own, is in [Set up APM](https://www.apitraffic.io/docs/apm/setup).

## Firewall options

With `@apitraffic/node` the firewall is always mounted and runs once your account has a live policy. These local controls exist mainly as overrides and kill switches.

| Option | Environment variable | Type | Default | Effect |
|---|---|---|---|---|
| `waf` (`@apitraffic/node` only) | `API_TRAFFIC_WAF_ENABLED` | `false` or an object | Unset | `false`, or the variable set to `false`, `0` or `off`, keeps the firewall off whatever your account says. An object passes the options below to the firewall without changing whether it runs. `true` means the same as unset. |
| `waf.enabled` | `API_TRAFFIC_WAF_ENABLED` | boolean | `true` | With `@apitraffic/waf` used directly: the master kill switch. |
| `waf.offlineWafMode` | `API_TRAFFIC_OFFLINE_WAF_MODE` | `'inherit'`, `'open'` or `'closed'` | `'inherit'` | What a server that has **never** loaded a policy does. `inherit` and `open` allow traffic; `closed` answers every request with `503` until a policy loads. The variable wins over the option. |
| `waf.onEvent` | - | function | - | Called for every inbound block or detection. |
| `waf.onEgressEvent` | - | function | - | Called for every outbound block or detection. |
| `waf.debug` | `API_TRAFFIC_DEBUG` | boolean | `false` | Log firewall decisions to the console. |

```javascript
app.use(apitraffic.express({
  waf: {
    onEvent: (event) => logger.warn({ waf: event }, 'firewall event')
  }
}));
```

> **Warning:** `offlineWafMode: 'closed'` is the only setting with which an ApiTraffic outage can stop your application serving traffic: a server that starts while ApiTraffic is unreachable, with no cached or pinned copy of your policy, refuses every request. If you use it, commit [offline definitions](https://www.apitraffic.io/docs/reference/offline-definitions). See [If ApiTraffic is unreachable](https://www.apitraffic.io/docs/reference/unreachable#the-firewall).

## Offline definitions

| Environment variable | Default | Effect |
|---|---|---|
| `API_TRAFFIC_DEFINITIONS_PATH` | Unset | Path of the pinned definitions file. When set, it is the only place the SDK and the CLI look. |
| `API_TRAFFIC_CACHE_DIR` | `node_modules/.cache/apitraffic` | Where the SDK keeps its last-known-good copy of your settings. |
| `API_TRAFFIC_FALLBACK_MAX_AGE_DAYS` | Unset (no limit) | Skip a pinned file older than this many days. |

See [Offline definitions](https://www.apitraffic.io/docs/reference/offline-definitions).

## Server labels

These variables label each server on the [Servers page](https://www.apitraffic.io/docs/integrations/servers) and on the requests it serves. The SDK checks each list in order and uses the first variable that is set, so common platform variables work without extra setup.

| Label | Variables, in order |
|---|---|
| Service | `API_TRAFFIC_SERVICE_NAME`, `SERVICE_NAME`, `K_SERVICE`, `FLY_APP_NAME` |
| Service version | `API_TRAFFIC_SERVICE_VERSION`, `SERVICE_VERSION`, `APP_VERSION` |
| Region | `API_TRAFFIC_REGION`, `AWS_REGION`, `AWS_DEFAULT_REGION`, `FLY_REGION`, `GOOGLE_CLOUD_REGION` |
| Commit | `API_TRAFFIC_GIT_SHA`, `GIT_SHA`, `GIT_COMMIT`, `SOURCE_VERSION`, `VERCEL_GIT_COMMIT_SHA`, `HEROKU_SLUG_COMMIT` |

A service version must be at most 64 characters of letters, digits, `.`, `_`, `+` and `-`; a commit must be 7 to 40 hexadecimal characters. Other values are ignored.

## Settings from your ApiTraffic account

These are configured in ApiTraffic and reach every connected server within seconds of a change. None of them needs a restart or a deploy.

| Setting | Where you change it | How it combines with local settings |
|---|---|---|
| [Redaction rules](https://www.apitraffic.io/docs/data/redactions) | **Account** → **Redactions** | Applied in your process before anything is sent, in addition to the built-in masking. Requests are held until the rules have loaded. |
| [Exclusions](https://www.apitraffic.io/docs/data/exclusions) | **Account** → **Exclusions** | Matching requests are not sent. |
| [Firewall policy](https://www.apitraffic.io/docs/waf/policies) | **Account** → **Firewall (WAF)**, on plans that include it | `waf: false` or `API_TRAFFIC_WAF_ENABLED=false` keeps it off locally. |
| [APM capture](https://www.apitraffic.io/docs/apm/capture-settings) | **Bucket Settings** → **APM Capture** | **Query Parameters** and **Redis Keys** add to local settings and can switch off automatically under **Auto-Disable**. **Code Location** applies unless set locally. |
| [Host telemetry](https://www.apitraffic.io/docs/integrations/servers#turn-on-host-telemetry) | **Bucket Settings** → **Host Telemetry** | Server-side only. Your plan sets the fastest reporting interval. |

## Per-request APIs

Call these from code that runs while a request is being handled: route handlers, middleware after ApiTraffic, and anything they call, including across `await`. Each one attaches to the request in progress. Outside a request they do nothing, and none of them ever throws into your code.

They are exported by `@apitraffic/node` and by each framework package. Import them from the package you installed:

```javascript
const apitraffic = require('@apitraffic/node');   // or '@apitraffic/express', and so on
```

### Tag a request

`tag(key, value)` attaches a searchable key-value pair, for example a customer or tenant ID, so you can find every request for that customer in the [request log](https://www.apitraffic.io/docs/traffic/search).

```javascript
apitraffic.tag('customerId', String(req.user.id));
apitraffic.tag('plan', req.user.plan);
```

- `key` is 1 to 64 characters: a letter first, then letters, digits, `_`, `.` or `-`. Keys starting with `waf.`, `redaction.`, `apitraffic.` or `at.` are reserved.
- `value` is a string, number or boolean; numbers and booleans are stored as text. Values longer than 255 characters are cut to 255. `null` or `undefined` adds no tag.
- A request holds at most 32 tag keys. Setting an existing key again replaces its value and does not count twice.
- A tag that breaks a rule is dropped and counted in [Settings → Dimensions](https://www.apitraffic.io/docs/tags#settings--dimensions), never thrown into your handler. See [the tag rules](https://www.apitraffic.io/docs/tags#the-tag-rules).

### Trace a code path

`trace(message)` records a timestamped note on the request, a console log you can read in production:

```javascript
apitraffic.trace('cart total exceeded the free-shipping threshold');
```

Each message is a string of at most 255 characters, and a request keeps at most 250 of them.

### Time your own work

`span(type, name, fn, meta?)` runs `fn`, times it and records it as a span on the request's waterfall. It returns exactly what `fn` returns, works with synchronous functions and promises, and re-throws `fn`'s error unchanged after marking the span failed. `time()` is the same function under another name.

```javascript
const rates = await apitraffic.span('http.out', 'tax lookup', () => taxClient.rates(cart), {
  host: 'api.acme-tax.example'
});
```

`startSpan(type, name, meta?)` is for work that does not fit one function. It returns a handle; call `end()` once when the work finishes:

```javascript
const span = apitraffic.startSpan('queue', 'publish order.created');
try {
  await queue.publish('order.created', order);
  span.end();
} catch (error) {
  span.end({ ok: false, meta: { error: error.code } });
  throw error;
}
```

- `type` is one of `db.query`, `redis`, `cache`, `http.out`, `queue`, `rpc`, `graphql` or `custom`.
- `meta` is an object of extra detail, redacted and capped at `maxSpanMetaBytes`. `setMeta(meta)` on a handle adds to it before `end()`.
- `end({ ok: false })` marks the span failed. Calling `end()` again does nothing.
- With `captureSpans` off, spans are not recorded and the function still runs.

### Read the request context

`getRequestManager()` returns the object holding the current request's tags, traces and spans, or `undefined` outside a request. `getContext()` returns the SDK's context store for the current request, or `undefined` outside one. Most applications never need either; prefer `tag`, `trace` and `span`.

### Process-level functions

| Function | Returns | Use it to |
|---|---|---|
| `stats()` | Delivery counters | Check that telemetry is leaving the process: in flight, queued, delivered, recovered, dropped by cause, and whether the circuit breaker is open. `@apitraffic/node` only; `@apitraffic/hapi` also exports `deliveryStats()`. |
| `flush({ timeoutMs })` | `Promise<{ flushed, remaining }>` | Deliver what has been captured without stopping, for example before a serverless function is frozen. Default timeout 2 seconds. |
| `shutdown({ flushTimeoutMs })` | `Promise<{ flushed, remaining }>` | Stop capturing, deliver what is queued, and release the realtime connection, timers and hooks so the process can exit. See [Shutting down](https://www.apitraffic.io/docs/reference/shutdown). |
| `embedToken(options)` | `Promise<{ token, expiresAt, tabs, features }>` | Mint a token for the [embeddable request log](https://www.apitraffic.io/docs/integrations/embed). Rejects on failure. |
| `escapeCriteriaValue(value)` | string | Escape a value you put in double quotes inside an embed `criteria` string. Prefer `filters.tags`, which escapes for you. |
| `register(options)` | `{ registered, reason }` | Start the APM bridge from code (`@apitraffic/node` only). Must run before your app loads the libraries it instruments. |

## Related

- [Node SDK](https://www.apitraffic.io/docs/integrations/node):
    Install and register the SDK for your framework.

- [Outbound requests](https://www.apitraffic.io/docs/outbound-requests):
    What is captured for the calls your app makes.

- [APM capture settings](https://www.apitraffic.io/docs/apm/capture-settings):
    The bucket switches that control what spans carry.

- [If ApiTraffic is unreachable](https://www.apitraffic.io/docs/reference/unreachable):
    Delivery limits and what happens during an outage.
