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.
init({ 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. |
The default UI
On the default setting the SDK renders a small floating bubble once this device has at least one conversation, and an inbox behind it. Nothing appears before the first thread exists — a user who has never filed a report never sees a bubble.
That bubble is the SDK’s entire ambient footprint. It is hand-written DOM, not a framework component, which is what keeps React out of the always-loaded bundle; the inbox itself is in a lazy chunk and is fetched when someone opens it.
Headless
To build your own inbox, take the state and render it yourself:
const traceitx = init({ apiKey: 'txx_live_…', replies: { ui: 'headless' } });
const stop = traceitx.threads.subscribe((state) => {
render(state.threads, state.unreadCount);
});
The thread API is on the handle:
| Call | Does |
|---|---|
list() | Every thread known to this device, synchronously. |
get(threadId) | Full thread with messages. |
reply(threadId, body) | Send a message. |
markRead(threadId) | Clear the unread flag. |
delete(threadId) | Remove it. Resolves false if the server could not be reached — offer a retry rather than navigating away. |
unreadCount() | Badge number. |
subscribe(cb) | Live updates; returns an unsubscribe function. |
refresh() | Force a poll now. |
Use headless 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.
Because the binding is per device, it lives in that browser’s storage: a different browser, or cleared site data, is a different device with no threads.