When replies are enabled, submitting a report mints a thread. Anything your team writes back reaches the reporter inside your app, without email.
Enabling
Server-gated, in the dashboard. The SDK reads it from
GET /api/config; the ingest response then carries
thread and device alongside the usual fields.
<TraceItXProvider
config={{
apiKey: 'txx_live_…',
replies: { pollIntervalMs: 60_000 },
}}
>
| Option | Default | Notes |
|---|---|---|
replies.disabled | false | No polling, no UI, and no reply token presented on submit. |
replies.ui | 'default' | 'headless' renders nothing — you build the UI. |
replies.pollIntervalMs | 60 000 | Floored to 60 s regardless of what you pass. |
With the default UI, a floating button with an unread badge appears once this device has a thread, and opens an inbox. Polling runs only while the tab is visible and idles when there are no open threads.
Headless
To build your own inbox, take the state and render it yourself:
<TraceItXProvider config={{ apiKey: 'txx_live_…', replies: { ui: 'headless' } }}>
The thread API is threads on useTraceItX(), with a live unreadCount
beside it: threads.list(), threads.get(id), threads.reply(id, body),
threads.markRead(id), threads.delete(id), threads.subscribe(cb) and
threads.refresh(). Use it when the default panel cannot match your product —
a support drawer you already have, or a design system the reporter has to live
inside.
Why the poll floor exists
A 60-second floor is not a rate limit dressed up as a default. Replies are a
conversation on human timescales, and a 5-second poll multiplies request volume
twelvefold to make a reply arrive a few seconds sooner. Passing 1000 gets you
60 000 — the SDK will not honour a value that costs your users battery for no
perceptible gain.
Privacy
A thread is bound to a device, not to a person. A reporter who never identified themselves can still receive replies, and you still do not know who they are — which is the point. See User identity for making that link deliberately.