A network breadcrumb tells you a request returned 500. A body tells you what it
returned. That is worth a lot in triage, and it is also the most sensitive thing
the SDK can capture — which is why it is off by default and gated twice.
Enabling
Server first, in the dashboard. The SDK reads it from
GET /api/config. Then, optionally, veto it locally:
init({ apiKey: 'txx_live_…', networkBodies: { disabled: true } });
A client can always turn this off and can never turn it on. If you have a
contractual reason bodies must not be captured in a particular build, set
disabled: true and it is off regardless of server state.
The allowlist
bodyContentTypes is an allowlist, not a blocklist. A body whose content
type is not listed is never read — not read then discarded, not truncated. That
is what keeps file uploads, images and binary streams out of reports entirely,
rather than relying on a size check to catch them.
What arrives
{
"ref": 17,
"t": 1765800000123,
"reqBody": "{\"couponCode\":\"SUMMER25\"}",
"resBody": "{\"error\":\"cart_empty\"}",
"resBodyTruncated": false,
"reqBodyBytes": 26,
"resBodyBytes": 24
}
ref joins to the network breadcrumb’s data.reqId. Bodies live in
payload.networkBodies, deliberately not inline on the crumb — a receiver that
only wants the timeline never has to parse them, and shedding bodies under budget
pressure does not disturb the crumb chain.
Why a body is missing
Each direction can carry a skip reason instead of content:
reqBodySkipped / resBodySkipped | Meaning |
|---|---|
content-type | Not on the allowlist. |
unsupported | The body could not be read — a stream, say. |
error | Reading it failed. |
The distinction matters: a missing body with no skip reason means the request
genuinely had none, while content-type means there was one and policy declined
it. A receiver can tell “no payload” from “not allowed” without guessing.
Byte caps and the total budget are in Server config.