Skip to main content
Problems are recurring root causes that Moda discovers by grouping signals (frustrations, tool failures, and other detections) across traces. Signal attributions land within minutes, and the ranked list is updated automatically as evidence accumulates. These endpoints mirror the Problems dashboard and add a feedback channel that steers discovery. Problem IDs are UUIDs. If a problem was merged into another, its old ID still resolves — responses report the surviving ID in problem_id and set alias_resolved: true.

GET /problems

The ranked problem list with coverage and freshness metadata.

Query parameters

integer
default:"30"
1–90. Bounds which computation runs count toward the list.
integer
default:"25"
1–25. This endpoint’s cap is 25.

Response fields

object
days_back, open_problems, reopened, total_problems, signals_attributed, explained_coverage_pct.
object
The tenant-wide coverage meter: attributed_signals, unexplained_signals, coverage_pct, historically_attributed_signals, historical_coverage_pct.
array
The trusted ranked list — only rows whose verification_status is current, ordered by rank. Each row has problem_id, display_name, cause_statement, lifecycle (open, fixed_monitoring, reopened, resolved), rank_score, rubric_version, total_attributions, family_weighted_count, affected_users, affected_conversations (traces affected), agg_prm_delta, abandonment_rate, recency_score, trend_score, multi_family, family_counts, coverage_overall, coverage_by_family, audit_pass_rate (or null), verification_status, verified_causal_version.
array
Same row shape as problems, for rows whose verification is failed, unverified, or stale. They are still live problems and still visible, but never ranked. Treat failed and unverified rows as hypotheses; stale rows rank again after re-verification.
object
macros[] — macro themes grouping leaf problems (problem_id, display_name, display_description, tier, child_count, children[]) — and unparented_leaf_count.
array
Provisional problems not yet in the ranked list: problem_id, display_name, display_description, family (or null), evidence_support (report_backed_members, attribution_count), verification_status, provisional: true.
object
When things were last computed: book_computed_at, latest_assignment_at, lag_seconds, discovery (latest session stats or null), verification (counts by status), remainder_backlog (pending_groups, pending_mass).
integer
Feedback rows submitted but not yet applied.

Example

This example is trimmed for readability: with limit=1 only the top-ranked row is shown, and the untrusted rows implied by the verification counts (stale: 1, unverified: 1) are omitted.

GET /problems/:id

Full detail for one problem. Returns 200 with found: false for an unknown or malformed ID — not 404.

Response fields

boolean
false when the ID does not resolve to a problem; all other blocks are then empty or null.
string / string / boolean
The surviving problem ID, the ID you requested, and whether a merged-away ID was folded to its survivor.
object | null
The same row shape as the list on GET /problems.
object | null
The latest problem definition: version, cause_statement, display_name, display_description, lifecycle, parent_problem_id, change_reason, match_criteria, counter_examples, scope_hints, updated_at, tier (macro or leaf), provisional, affected_surface, causal_content_version.
array
Rank history: {run_ts, rank_score, total_attributions, rubric_version, coverage_overall} per computation run, newest first.
array
Child problems for macro problems: {problem_id, display_name, cause_statement, lifecycle, version}.
object
audit_pass_rate, judge_audit ({passed, total}), counterfactual_replay ({passed, total}), and samples[] — individual verification checks with kind, passed, rubric_version, conversation_id, segment_id, rationale, verified_at.
array
A first page of evidence rows (see /problems/:id/evidence for the full paginated set and field list).
array
Per-trace {conversation_id, user_id, signal_count, family_counts, last_signal_at} rollups.
object
The same tenant-wide coverage meter as the list endpoint.
object
First page of investigation reports: {items[], has_more, next_cursor} (full set on /problems/:id/reports).
object
Verification confidence for the current problem version: current_rubric_version, causal_content_version, verification_status, audited_causal_content_version, per-version breakdowns (current, stale), and passed/failed samples.
array
Lineage events: {problem_id, event_id, event_kind, related_problem_id, rubric_version, causal_content_version, reason, created_ts}.
object
known_users, unknown_identity_conversations, total_conversations, identity_coverage_pct — impact is split so traces without a known user are never reported as zero users.
object
tier, parent (or null), children[].
object | null
Precomputed narrative blocks (executive_summary, causal_chain, suggested_investigation, representative_story_keys, limitations) plus computation metadata. null until a dossier has been generated.
array
Representative examples, each {story_kind, evidence, verification} where story_kind is one of top_confidence, ground_truth_passed, disputed, replay_verified, newest.

Example

The example trims header to a few fields for readability; the live response returns the complete list-row shape documented under GET /problems.

GET /problems/:id/reports

Investigation reports for one problem, keyset-paginated.

Query parameters

integer
default:"20"
1–50.
string
The next_cursor from a previous page.
Each report has report_id, conversation_id, segment_id, status (investigated, no_problem, or rejected), problem_found, mechanism, narrative, citations[] ({path, ref, quote}), signals_explained, signals_total, signals_explained_ids[], signals_unexplained_ids[], suspected_surface, severity, confidence, primary_family, investigated_at.

GET /problems/:id/conversations

Traces attributed to one problem, keyset-paginated, with per-trace counts and an anchor for deep-linking.

Query parameters

integer
default:"25"
1–50.
string
The next_cursor from a previous page.
string
Filter to one signal family (for example tool_failure, emotion).
string
How the trace joined the problem: rubric_match or local_causal_chain.
Use latest_anchor.anchor_msg_index with /conversations/:id/context to read the moment the signal fired.

GET /problems/:id/evidence

All evidence attributions for one problem, keyset-paginated. Rows have the same shape as the evidence block on GET /problems/:id: attribution_id, conversation_id, segment_id, anchor_signal_id, anchor_msg_index, signal_family, signal_type, confidence, causal_rationale, door, replay_verified (or null), evidence_snippet, dedup_count, user_id, assigned_at, snippet_parsed (structured parse of evidence_snippet, null for legacy raw-text rows), snippet_legacy_raw_text, report_id (or null for fast-lane rows).

Query parameters

integer
default:"25"
1–50.
string
The next_cursor from a previous page.

POST /problems/:id/feedback

Records feedback on a problem. Feedback does not edit the problem directly — it is incorporated automatically. mark_fixed also arms automatic reopening if the problem recurs. This is the endpoint behind moda problem-feedback.

Body fields

string
required
One of mark_fixed, dismiss, flag_attribution, rename.
string
Up to 2,000 characters. Required for dismiss and flag_attribution.
string
Required for flag_attribution. A non-UUID value returns 400; a UUID that does not belong to this problem returns 404.
string
Up to 255 characters. Required for rename.
string
default:"data-api"
Recorded as the acting identity, up to 255 characters.
This endpoint is idempotent: feedback_id is derived deterministically from the payload, so resubmitting identical feedback while it is still pending returns the same feedback_id with duplicate: true and records nothing new. Safe to retry.
A malformed (non-UUID) problem ID returns 400.

GET /problems/:id/feedback

Feedback history for one problem, with pending/consumed state.

POST /feedback

General feedback about data quality — a wrong cluster label, a mismatched frustration cause, missing data. This is separate from problem feedback (which moda problem-feedback sends): it flags issues with what the Data API returned, and it is what moda feedback sends. Returns 202.

Body fields

string
required
One of bad_cluster_label, mismatched_frustration, missing_data, noisy_data, wrong_tool_failure, incorrect_loop, api_quirk, other.
string
default:"low"
One of info, low, medium, high.
string
Up to 4,000 characters describing what looked wrong.
string
One of cli, sdk, ui.
string
The agent submitting the feedback, up to 64 characters.
string
Up to 32 characters.
object
Free-form references, for example {"conversation_id": "...", "tool_name": "..."}.
object
Free-form context about the command or query that produced the suspect result.
Unlike problem feedback, this endpoint is not idempotent — every request creates a new feedback record with a random feedback_id. Do not retry it automatically.

Next steps

  • Dashboard: Problems — the same problem book as a kanban with dossiers.
  • Signals — the raw detections that problems are built from.
  • Traces — read the context around any evidence anchor.

Problem impact within use cases

GET /api/v1/data/problem-impact connects attributed problem evidence to the use-case segments in which it occurred. Authenticate with the same API key as other Data API endpoints; tenant scope comes from that key. Select a node, a problem, or both. catalog lists available use cases and problems. overview returns the distinct affected share for root use cases (or the selected node’s children). A parent problem includes its current leaf problems. affected-use-cases returns affected counts without full population denominators. trace-use-cases resolves current use-case membership from a problem’s attributed segments for the specified traces across their history; it does not apply the days window. The response includes runId, resolved problemId, window, nodes, problems, rows, evidence, nextCursor, and computedAt. Metric rows have node_id, problem_id, and period (current or previous). An empty problem_id identifies the full analyzed population; * identifies the distinct union of affected traces across selected problems. Use the empty-ID row’s analyzed denominator for each problem’s percentage. A problem-only metrics response includes only affected use cases, with each denominator covering that use case’s full analyzed population. Never add problem counts to produce a distinct total. Trend rows include a UTC bucket date and span the current and preceding windows, or all available history for days=all without a preceding comparison window. Missing dates have no analyzed population and no rate. affected / analyzed is affected share, not task failure rate. traces counts distinct conversations with matching current segments. analyzed counts conversations containing at least one matching analyzed segment, including negative analysis outcomes. segments and analyzed_segments expose partial analysis coverage. users counts distinct nonempty identities on affected evidence; identified_traces provides identity coverage. Evidence returns up to 25 distinct conversations per page with the exact segment_id and zero-based anchor_msg_index. Continue using nextCursor, runId, window.to, and the original filters. A cursor from another scope is rejected. Unmapped evidence is excluded from scoped results. These are live reads of the current classification and attribution records, using segment occurrence timestamps. They are not stored historical snapshots: regeneration, merges, transfers, and retractions can change past results. Individual source reads can observe concurrent pipeline updates. 409 requests a classification refresh; query failures remain errors, never zero-count results. No materialized impact counters or refresh jobs are added.