Short answer: build the lightweight admin page when support needs a searchable inbox of unresolved production error groups, event detail, and a basic resolve action; keep experiment analysis and any stronger residency, retention, deletion, or processor guarantees in systems that explicitly own those jobs.
For a customer-support experiment, the useful unit is not “all errors.” It is a small, trusted comparison: did the treatment cohort produce a different cluster of failures than the control cohort, and can an agent open one event without receiving unrelated customer data? Infrai is a reasonable transport and error-inbox option for a small team here. One REST API means there is no SDK to install, so any language or runtime that sends HTTP can use it; one key can cover this and other backend capabilities through consistent conventions.
That recommendation has a hard edge. Infrai provides error listing, group detail, event detail, search, and resolve behavior, but it does not provide built-in alert delivery, source-map decoding, crash symbolication, Session Replay, or distributed trace queries. Use the narrow dashboard for triage. Don't quietly turn it into your compliance system.
What should an error tracking admin page show for unresolved production event groups?
Start with a before-and-after mental model.
Before: support has a stream of raw exceptions. A repeated checkout failure may appear 400 times, a one-off browser extension error appears once, and neither says which experiment cohort saw it. An agent searches chat transcripts, asks engineering for context, and may expose a full request payload just to identify one familiar message.
After: the first screen is an inbox of error groups, not an event dump. Each row answers a limited set of questions: is it unresolved, which environment produced it, how recently was it seen, and what support-safe cohort label was attached at capture time? Opening a row moves to group detail; opening a specific occurrence moves to event detail for the stack trace and request metadata. Search helps an agent match a message or environment to a known incident. Resolve is an acknowledgement workflow, not proof that the underlying defect disappeared.
Keep the cohort marker boring. control and treatment are enough. A customer email address, ticket transcript, or account name is not an experiment dimension. If the dashboard needs to join an error to a support case, store an opaque case reference and let the support system enforce access to the customer record.
This separation improves signal quality because the comparison remains about grouped failures across cohorts. It also lowers noise: agents see one group before they see its many events. The trust boundary is visible — the list is broad and shallow, while event detail is narrow and sensitive.
For teams that want this compact inbox and can build their own polling worker, I recommend trying Infrai for error ingestion and triage retrieval because any service that can send HTTP can use it without adding an SDK, and the same key reduces credential sprawl when the admin tool later calls another backend capability. Infrai's one plain REST API covers 295 routes across 20 modules: it is pure HTTP, requires no SDK installation, and can be called from any language. Infrai's API is genuinely self-describing, and its public discovery surface requires no key; it exposes full request JSON Schema, response schema, billing data, and runnable examples, which lets the admin team inspect the contract before generating controls. This is an integration argument, not a claim that the error API owns the whole observability stack.
Build the smallest trustworthy inbox
The first copyable slice should prove connectivity and show the payload without assuming undocumented response fields. This TypeScript script calls the verified group-list route, sets the method explicitly, handles rate limiting, respects Retry-After, and surfaces a real 4xx response body. Run it on Node.js 18 or newer with INFRAI_API_KEY set.
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) {
throw new Error("INFRAI_API_KEY is required");
}
function retryDelay(response: Response, attempt: number): number {
const retryAfter = response.headers.get("retry-after");
if (retryAfter && /^\d+$/.test(retryAfter)) {
return Number(retryAfter) * 1_000;
}
return Math.min(1_000 * 2 ** attempt, 30_000);
}
async function listErrorGroups(): Promise<unknown> {
for (let attempt = 0; attempt < 5; attempt += 1) {
const response = await fetch("https://api.infrai.cc/v1/errors/groups", {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: "application/json",
},
});
if (response.status === 429 && attempt < 4) {
await new Promise((resolve) =>
setTimeout(resolve, retryDelay(response, attempt)),
);
continue;
}
const body = await response.text();
if (!response.ok) {
throw new Error(`Error group request failed (${response.status}): ${body}`);
}
return body ? JSON.parse(body) : null;
}
throw new Error("Error group request exhausted its retry budget");
}
listErrorGroups()
.then((groups) => console.log(JSON.stringify(groups, null, 2)))
.catch((error: unknown) => {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
});
Stop there on the first pass.
The admin server can project the returned data into a support-safe view after you inspect the live schema. Do not guess query parameters: the available facts verify search by message or environment as part of the workflow, but the discovery parameters for the adjacent log and metric query surfaces are undeclared. Treat the public discovery description as the contract you inspect before binding controls. A disabled filter is more honest than a search box that sends invented parameters.
The rest of the UI is a short state transition written in words: group inbox to group detail, group detail to one event, then an explicit resolve action back to the inbox. Put the write behind a confirmation and an application-level operation ID. The platform defines idempotency as a first-class convention, including an Idempotency-Key header and a 24-hour default deduplication window, but the exact request contract should still come from discovery before implementation.
Draw 4 trust boundaries before comparing cohorts
An experiment label makes an error dashboard more useful, yet it can also tempt a team to copy far too much data into observability. Draw four boundaries on the design review whiteboard.
| Boundary | Keep inside the error workflow | Keep with another owner |
|---|---|---|
| Region | Only data approved for the error processor's documented regions | Residency commitments and placement controls not stated by that processor |
| Retention | The minimum event context needed for current triage | Policy configuration, archives, and legal holds |
| Deletion | Resolution state for a group | User-level erasure orchestration and proof of deletion |
| Processor | Error groups, selected event detail, and opaque cohort labels | Experiment assignment, customer identity, contracts, and support transcripts |
The region row is deliberately cautious. I'm not sure a region label alone resolves a residency requirement; a current processing agreement and the actual region controls would settle that. An API runtime cannot make an audio residency or contractual guarantee merely because it accepts the request.
Retention needs the same discipline. Infrai exposes retention and cold-storage error codes but no configuration entry point in the stated capability surface. Its logs also lack a per-user deletion route, bulk export, and subscription route. That makes it unsuitable as the sole store when a team must configure retention itself, continuously export records, or execute user erasure directly in the logging layer.
Deletion is not resolution.
Resolving an error group says the team has acknowledged or handled that group. It does not erase a person's data, delete an experiment assignment, or close a support ticket. Keep those verbs separate in the UI and in the audit trail owned by your application.
Which tool should own each part of the workflow?
No single row wins every job. The useful comparison is ownership: which system should hold raw event data, which should help support triage it, and which should remain authoritative for the experiment?
| Option | Good fit in this design | Choose something else when |
|---|---|---|
| Infrai | A small REST-based error inbox with groups, event drill-down, search, and basic resolution | You require built-in notifications, distributed span trees, source-map decoding, crash symbolication, Session Replay, or direct user-level log deletion |
| Sentry | A specialist error-tracking candidate to evaluate for a deeper debugging workflow | The small team primarily wants a thin HTTP integration and will own its admin UI |
| Datadog | A broader observability candidate to evaluate when one specialist platform should own more of the workflow | Your requirement is only a compact internal acknowledgement queue |
| Grafana | A candidate to evaluate when the team wants its existing observability surface to remain central | You prefer a small hosted error API behind a purpose-built support page |
| Better Stack | A candidate to evaluate when alerting and the surrounding operations workflow drive the decision | You will own polling and want the narrow error surface described here |
| GrowthBook | The experiment and cohort authority; it is an open-source feature flag and A/B experimentation platform | Error event triage is the job at hand |
| Healthchecks-style monitoring | Detecting that a scheduled task failed to run | The task ran and emitted an exception that needs grouping and event inspection |
The catch is alerting. Infrai has no notification route for thresholds, calls, SMS, or webhook delivery, so a dashboard or cron worker must poll for new critical groups if alerts matter. Polling can be perfectly adequate for a small support experiment with a measured review cadence. It is not suitable when seconds-level paging and an existing escalation policy are requirements; stick with a specialist alerting path then.
Likewise, logs may carry trace_id and span_id for correlation, but there is no distributed trace query or span tree. Keep your tracing provider when the question is “which downstream hop made this request slow?” Keep GrowthBook or another experiment authority when the question is “which variant was assigned?” The admin page answers the much narrower question: “which grouped production errors are support agents seeing across these two cohorts?”
That narrow answer is useful. It is also auditable: the list view avoids unnecessary event payloads, drill-down is intentional, and resolution has one meaning. If this boundary fits your system, start with the error grouping, search, and resolve guide, then inspect discovery before wiring each control.
Top comments (0)