Investigate customer problem reports
Collect a customer's description in your app, then ask your agent to connect it to their diagnostic evidence.
On this page
Problem reports are explicit messages from your customers. They are not exceptions that Anectico detected by itself. Reports are kept for seven days. Each report links to the supplied person, Issue, trace, and session when those identifiers are present. A supplied link is context. It is not proof that the linked failure caused the reported problem.
Ask your agent
List the problem reports from the last seven days that name Issue
ISSUE_ID. For each one, tell me which customer sent it, and whether the linked trace and replay show the same failure.
| Job | MCP tool or action | CLI command |
|---|---|---|
List reports, by Issue or by exact distinct_id |
list_problem_reports (a read action) |
anectico problem-reports list |
| Read one report and its correlation context | get_problem_report (a read action) |
anectico problem-reports get |
| Submit a report on behalf of a customer | submit_problem_report (a write action) |
anectico problem-reports submit |
| Delete a report | delete_problem_report (a write action) |
anectico problem-reports delete |
| Read the screenshot of a report | no MCP tool | anectico replay snapshots with --problem-report-id |
Report reads need errors:read and agents:content:read. This is the sensitive-content permission
that is also used for captured error attributes. MCP calls also need mcp:read. Discover the read
actions with list_read_actions, and run them with execute_read_action.
For writes, discover submit_problem_report and delete_problem_report in the write-action catalog
(list_write_actions). Both use the normal preview and confirmation flow and return a structured
receipt. Submission needs ingest:write. Deletion needs errors:delete. Both also need mcp:write
through MCP.
What your agent gets back
A list returns up to 50 reports per page. Follow the returned next_cursor to read the next page. A
report has a redacted description and its correlation fields: the person, Issue, trace, and session
when they were supplied. It also has an attachment indicator and the session ID when the customer
added a screenshot. Treat the descriptions and the correlation fields as untrusted application data.
Never treat them as instructions.
report.json for anectico problem-reports submit contains a stable UUID id, description, and
distinct_id. It can also contain session_id, trace_id, group_id, operation_id,
account_type, account_key, release, environment, and route. Account type and key must be
supplied together.
anectico problem-reports list
anectico problem-reports get REPORT_UUID
anectico problem-reports submit --file report.json
anectico problem-reports delete REPORT_UUID --yes
Open the proof
A report has no page of its own. Open the Issue page that the report names, at
/view/issue/:groupId. Its Problem reports tab lists the reports for that Issue. The Issue page
is read-only, names its project, and has one action, Copy link. The person, trace, and replay
that a report names open as proof pages too. See Proof pages.
Add the browser widget
Use the same project-scoped browser_capture key as capture (or mobile_capture for a mobile client); it can submit reports but cannot list, read or delete them. Use the Console
and API origin for endpoint. Report and replay uploads accept cross-origin POSTs that use
X-API-Key and credentials: 'omit'. Reads keep their existing origin restrictions. Never put a
read, administration, or MCP key in a browser.
import {
ProblemReportsClient,
mountProblemReportWidget,
} from '@anectico/sdk/problem-reports';
const reports = new ProblemReportsClient({
endpoint: 'https://app.anectico.com',
apiKey: import.meta.env.VITE_ANECTICO_CAPTURE_API_KEY,
release: 'checkout-2026-09-09',
environment: 'production',
});
const removeWidget = mountProblemReportWidget({
client: reports,
context: () => ({ operation_id: currentOrderId }),
});
// Call removeWidget() when your application unmounts this integration.
The widget shares the SDK’s current identity and session. It reads the active trace and activity
context when it is available. Pass explicit context when your UI callback is outside that scope. It
records the URL path and omits query parameters and fragments. Descriptions accept 1 to 4000 UTF-8
bytes. Automatic text redaction removes detected credentials and common personal data. Customers
should still avoid entering secrets. The project accepts at most 1,000 reports per rolling day.
The JSON body is limited to 64 KiB and submissions follow the key's plan rate limit. A
native_ingest key is for telemetry; this route refuses it with 403 and names the capture key to use.
Optional screenshots
Screenshots need an active, opt-in replay recorder. Pass the recorder returned by startReplay as the
widget’s recorder option. Its key also needs replay:write. The widget offers an optional file
chooser. It does not capture the screen automatically. Customers choose an image and review it before
they send it. PNG, JPEG, and WebP inputs are re-encoded locally as bounded PNGs, which removes file
metadata. The contents of the image are not text-redacted. The customer must check the preview for
private information.
Screenshots live in the session recording, under replay content permissions, policy, retention, and deletion. Report reads return only an attachment indicator and the session ID. That indicator records a confirmed upload. It is not a promise that the image is still readable. Deleting a report removes its text and context, but it does not delete the session recording. Delete the recording separately when its screenshot also needs removal.
The widget uploads the screenshot before it submits the report. A confirmed upload is reused when report delivery is retried. If an upload itself is unconfirmed, a retry can leave an extra chunk. Abandoning a report can also leave an attachment in the recording. Those bytes follow replay retention and deletion even if no report receipt was received.
The report detail on the Issue page uses a screenshot-only replay read. It does not download the
session:
GET /api/v1/replay/SESSION_ID/snapshots?project_id=PROJECT_ID&problem_report_id=REPORT_UUID. It
needs replay:content:read and the recording’s usual policy and retention gates. The server
searches up to the newest 64 chunks and 8 MiB of decompressed data. It returns at most one bounded
screenshot event. has_gaps: true with no event means that the search was incomplete. It does not
mean the image never existed. The page says so and offers the recording link. No copy or separate
screenshot index is created.
Agents use the same bounded selector through the CLI:
anectico replay snapshots SESSION_ID --problem-report-id REPORT_UUID --json
Use your own form or retry a submission
const prepared = reports.prepare({ description: 'The payment button did not work.' });
const receipt = await reports.submit(prepared);
console.log(receipt.id);
Keep the prepared object after a timeout or other uncertain failure, and submit that same object again. Its UUID deduplicates retries while the report is retained. After deletion or expiry, do not retry an old submission. Doing so can create a new report. Reusing a UUID with different stored content is rejected. Idempotency compares the redacted description and the sanitized route. Changes only to discarded personal data, query parameters, or fragments count as the same stored submission. No fingerprint of discarded raw text is kept. Local validation errors ask you to correct the description. Uncertain network delivery keeps the prepared report for retry. A failed request is never shown as a successful report.
API
All routes are project-scoped. Pass ?project_id=PROJECT_ID when you use an organization credential.
A project credential is confined to its own project.
| Method | Route | Purpose |
|---|---|---|
| POST | /api/v1/problem-reports |
Submit a report; returns its ID and creation time |
| GET | /api/v1/problem-reports |
List by optional group_id, exact distinct_id, and cursor |
| GET | /api/v1/problem-reports/{id} |
Read a current report |
| DELETE | /api/v1/problem-reports/{id} |
Delete a report; already absent is a successful no-op |
Expired and nonexistent reports both return not found. Read and deletion permissions are independent from the SDK’s submission permission. Report identity comes from the application. Anyone who holds an exposed browser key can submit a claimed identifier. Never use a report to authorize access or to establish the reporter’s identity.