> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apitraffic.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Capture Settings

> Collect SQL parameters, Redis keys and code locations when you need them — and only then

APM is deliberately conservative about what it collects. Two pieces of detail are useful while you
are investigating something and risky to leave on forever, so each is a switch that is **off by
default** and can be turned on for a **time-boxed window**. Code location is different: it is about
your code rather than your data, so it is **on by default** and stays on until you turn it off.

| Setting | Default | What it adds |
| - | - | - |
| Query parameters | off, time-boxed | The statement exactly as issued, plus its bind values |
| Redis keys | off, time-boxed | The concrete key instead of the masked shape |
| Code location | **on** | The file, line and function that started the operation, plus its callers |
| Stack trace depth | **10 frames** | How many of your own frames travel with each span |

## What is collected by default

Without any of these switches:

* SQL is **normalized before it leaves your server**. `where id = 42` is captured as
  `where id = ?`, and the bind values are discarded in-process — they are never transmitted or
  stored.
* Redis and cache keys are **masked to their shape** — `user:9f31:profile` becomes
  `user:?:profile`.
* Spans do carry the code that started them (see [Code location](#code-location)); switching it off
  removes the location, the function name and the stack from every span.

Operations are grouped by their normalized shape either way, so **turning a switch on or off never
splits an operation's history** in the Performance analytics.

## Statement text is stored only for accounts that opt in

The SDK sends the normalized statement and the caller stack with each span, but ApiTraffic
**stores and shows them only for accounts with statement text enabled**. For every other account
they are removed before the request is stored, and removed again whenever a request is read, so
the request page, shared links, embeds, workflows, the API and the MCP tools all show the
operation, its fingerprint and its code location without the query text. This also applies to the
statement as issued and its bind values from *Query parameters*: that capture only shows anything
for an account with statement text enabled. Operators can switch statement text off for every
account at once.

Requests stored before this rule had text attached; it is withheld on read in the same way.

Opting out is final for the text captured until then: when an account turns statement text off,
the moment is recorded, and text captured before it (plus a two-minute grace for servers that had
not yet seen the change) is never shown again, even if the account later opts back in. Span
reports over a window that reaches back before that moment show only the statement text captured
since.

## Turning capture on for a bucket

**1. Open the bucket**

Go to **Bucket Settings** and select the **APM Capture** tab.

**2. Enable what you need**

Turn on *Collect SQL statements with their parameters* and/or *Collect concrete Redis and cache
keys*. *Collect the code location that started each operation* is already on, with a **Stack
trace depth** of 10 frames.

**3. Choose an auto-disable window**

Under **Auto-Disable**, pick 15 minutes, 1 hour, 4 hours, 24 hours, or *Until I turn it off*.
The page then shows the exact time capture stops. The window restarts each time you save, and
applies to the parameter and key switches only — never to code location.

**4. Save**

Connected servers pick the change up immediately over the realtime channel — no restart and no
deploy. Reproduce the problem, then let the window expire.

> [!WARNING]
>
> **Use these with care.** Parameters and concrete keys routinely contain personal data,
> identifiers, tokens and secrets, and whatever is collected is stored with the request and shown
> in the waterfall. Your account [redactions](https://docs.apitraffic.io/api-reference/redactions.md) still apply, and captured
> values are the first thing dropped when a span exceeds its size budget — but the safest posture
> is a short window, deliberately opened.

### How the window is enforced

The expiry is applied in two independent places, so capture stops on time even if a server never
reloads its settings and even if nobody returns to the dashboard:

* the API stops reporting the switches as enabled on the SDK settings payload once the window
  passes, and
* each SDK arms its own timer and returns capture to the defaults at that exact moment.

## Turning capture on in the SDK

The same switches exist as [SDK options](https://docs.apitraffic.io/apm/setup.md#configuration) — `captureStatementParams`,
`captureRedisKeys`, `captureCallSite` and `captureStackDepth`, or their `API_TRAFFIC_*` environment
variables.

For the two privacy switches the effective setting is **local OR bucket**: a bucket switch turns
capture on for servers that have not enabled it locally, and a locally enabled switch is *not*
turned off when a bucket window expires.

Code location and its depth are **owned by the bucket** unless the application sets them
explicitly. Set `captureCallSite: false` (or `captureStackDepth: 1`) in code or the environment and
that wins everywhere; leave them alone and the dashboard is in charge, including switching capture
off. Prefer the bucket settings for production — they are reversible and do not need a deploy.

## Code location

A span carries `callSite` (for example `src/services/orders.ts:142:9`), `callSiteFn`, and
`callStack` — the callers above the call site, innermost first — shown in the span details as
**Code location**, **Function** and **Stack trace**. The stack is taken when the operation is
*initiated*, because a driver callback runs far from the caller.

Frames in Node internals, `node_modules` and the ApiTraffic packages are skipped, so the depth is
spent on the chain through your own code: the route handler that led to the query, not the driver
that ran it. Paths are relative to your application root, and transpiled projects report the built
file unless source maps are deployed.

Frames are read through V8's structured stack API rather than by formatting and parsing a stack
string, which is what makes ten frames affordable as a default: measured per span, \~12 µs for ten
frames against \~16 µs for the single-frame string walk this replaced. The cost follows how deep the
stack actually is, not the depth you ask for — an operation issued through an ORM under an
instrumentation patch has far more library frames to walk past, and costs nearer \~30 µs.

Lower the depth if you are counting microseconds on a very hot path — `1` keeps the call site alone
— or switch it off entirely, in which case no stack is walked at all rather than walked and
discarded. If a span exceeds its metadata budget the outermost frames are dropped first, so the call
site always survives.

The location is also stored on the span row, so **Code Location** is available as a grouping in
[Performance → Operations](https://docs.apitraffic.io/apm/analytics.md#operations) and not just in a single request. Where a
[git integration](https://docs.apitraffic.io/git-integrations/overview.md) has correlated a deploy to the request, the location links
to the file at the exact commit that was running — a permalink, never a branch, so it still points
at the right line after the code has moved on.

## Managing capture over the API

The switches are fields on the bucket, so they can be driven from CI or a script as well as the
dashboard.

```bash theme={null}
curl -X PUT "https://api.apitraffic.io/v1/accounts/{accountSid}/buckets/{bucketSid}" \
  -H "Authorization: Bearer your-jwt-token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production API",
    "verifySslCerts": true,
    "apmCaptureStatementParams": true,
    "apmCaptureExpiresAt": "2024-01-15T18:42:00Z"
  }'
```

**apmCaptureStatementParams** `boolean`

Allow spans to carry full SQL statements and their bind parameters.

**apmCaptureRedisKeys** `boolean`

Allow spans to carry concrete Redis and cache keys.

**apmCaptureCallSite** `boolean` (default: true)

Whether spans carry the application file, line, function and caller chain that started the
operation. `false` collects none of it.

**apmCaptureStackDepth** `number` (default: 10)

How many application frames to keep per span, innermost first (1–50). `1` keeps the call site
alone.

**apmCaptureExpiresAt** `string`

ISO 8601 time (UTC) at which the parameter and Redis key switches stop applying. `null` means they
stay on until turned off. Code location is unaffected.

The effective values, after expiry has been applied, are what SDKs receive on their settings
payload:

```json theme={null}
{
  "apm": {
    "captureStatementParams": true,
    "captureRedisKeys": false,
    "captureCallSite": true,
    "captureStackDepth": 10,
    "expiresAt": "2024-01-15T18:42:00.000Z"
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.