---
title: "Get a scenario"
method: GET
path: "/projects/{projectId}/user-testing/scenarios/{scenarioId}"
tags: ["User testing"]
---

# Get a scenario

`GET /projects/{projectId}/user-testing/scenarios/{scenarioId}`

The scenario, plus the insights envelope over its latest analyzed window when the caller may have it.

Project members only. This route is deliberately outside the guest allowlist, and the envelope additionally needs workspace membership — share-link visitors can never read other visitors' evidence.

## Response `200`

The scenario.

- UserTestingScenarioDetail — A published scenario, enriched with findings over its latest analyzed window. NO `accessVersion`: the settings envelope this is read from does not carry it — the publish and rebind responses do.
  - `id` string, required
  - `projectId` string, required
  - `name` string, nullable, required
  - `description` string, nullable, required
  - `mode` string, nullable, required
  - `environmentId` string, nullable, required
  - `insights` InsightsEnvelope — The common insights envelope, shared by eval runs, swarm waves and user-testing windows. One shape for three producers, so a caller writes the reading code once. An ABSENT envelope and `status: "not_available"` mean the same thing and both are normal: the field is an enrichment, and a caller who may not have it gets the resource without it rather than an error.
    - `schemaVersion` 1, required
    - `scope` InsightScope, required — What this envelope is about. The extra fields depend on `kind`.
      - `kind` 'eval_run' | 'swarm_wave' | 'user_testing_window', required
      - `id` string, required
      - `runId` string — `swarm_wave` only.
      - `scenarioId` string — `user_testing_window` only.
      - `windowStartAt` integer — `user_testing_window` only.
      - `windowEndAt` integer — `user_testing_window` only.
    - `status` 'not_available' | 'not_requested' | 'pending' | 'completed' | 'failed', required — `not_available` means this deployment cannot produce insights at all — treat an ABSENT envelope the same way. `not_requested` means nobody has asked. `pending` means one is running: poll, do not re-request.
    - `reasonCode` string, nullable, required
    - `retryable` boolean, required — Whether asking again could produce a different answer. False on a `failed` envelope means the input, not the attempt, was the problem.
    - `error` object, nullable, required
      - `code` string, required
      - `message` string, required
    - `generatedAt` integer, nullable, required
    - `updatedAt` integer, nullable, required
    - `summary` string, nullable, required
    - `coverage` object, required — READ THIS BEFORE QUOTING ANY FINDING. `truncated` and `lowConfidence` are the difference between "this happens" and "this happened in the part we looked at".
      - `unit` 'iterations' | 'sessions', required
      - `analyzed` integer, required
      - `total` integer, required
      - `gradedCount` integer
      - `feedbackCount` integer
      - `truncated` boolean, required — The analysis saw `analyzed` of `total`, not all of it.
      - `lowConfidence` boolean, required — Too little was analyzed to generalize. Findings still stand as observations of what WAS seen.
    - `findings` ActionableFinding[], required
      - `id` string, required — Stable remediation id (`rf_<16 hex>`). Survives dynamic error values, so the same problem keeps the same id across runs — dismiss it once and it stays dismissed.
      - `signalFingerprint` string, required — The registry signal this derives from. Several findings can share one.
      - `title` string, required
      - `category` 'unknown' | 'tool_contract' | 'tool_runtime' | 'capability_gap' | 'workflow' | 'agent_behavior' | 'test_design' | 'environment', required
      - `attribution` 'unknown' | 'server_contract' | 'server_runtime' | 'server_capability' | 'agent_or_prompt' | 'test_design' | 'environment', required — WHOSE problem this is. `server_*` points at the MCP server; `agent_or_prompt` and `test_design` point back at the caller.
      - `actionTarget` 'investigate' | 'mcp_server' | 'agent_configuration' | 'eval_case' | 'environment', required — What you would change to fix it.
      - `actionability` 'informational' | 'investigate' | 'ready', required — `ready` means the finding names a specific target and change. `investigate` means it does not yet. `informational` means there is nothing to do.
      - `severity` 'info' | 'low' | 'medium' | 'high', required
      - `confidence` 'low' | 'medium' | 'high', required
      - `observed` string, required — DETERMINISTIC observation — counts and identities, never model prose. This is the part you can verify yourself.
      - `rootCause` string
      - `recommendation` string, required
      - `acceptanceCriteria` string[], required — How you would know the fix worked.
      - `affected` object, required — How much of the analyzed population hit this. Read it as a ratio — `1/40` and `38/40` are different problems.
        - `count` integer, required
        - `total` integer, required
        - `unit` 'iterations' | 'sessions', required
      - `patternSlug` string
      - `target` object — Present only when a server (and, for tool surfaces, a tool) resolved against the pinned snapshot. Required for `mcp_server` / `ready`.
        - `serverId` string, required
        - `toolName` string
        - `surface` 'description' | 'input_schema' | 'output_schema' | 'handler' | 'server_instructions' | 'capability', required
        - `fieldPath` string
        - `snapshotHash` string, required — The pinned snapshot the target resolved against, so a finding cannot silently re-point at a definition that changed after it was written.
        - `currentDefinition` object
          - `description` string
          - `inputSchemaJson` string
          - `outputSchemaJson` string
          - `truncated` boolean, required
      - `evidence` ActionableFindingEvidence[], required
        - `sessionId` string
        - `iterationId` string
        - `kind` 'tool_error' | 'transcript' | 'feedback' | 'judge' | 'contrast', required
        - `excerpt` string, required — Scrubbed and clipped at the producer. Never a full transcript.
        - `toolName` string
        - `errorCode` string
    - `runHealth` object — Swarm only. Launch outcomes never appear as findings — a run that could not start is an operational fact, not something the server under test did.
      - `targets` object[], required
        - `subjectKind` 'environment' | 'host', required
        - `subjectId` string, required
        - `subjectLabel` string, required
        - `attempted` integer, required
        - `succeeded` integer, required
        - `failed` integer, required
        - `rateLimited` integer, required
    - `truncation` object, required — What this RESPONSE dropped to stay a sane size, as distinct from what the ANALYSIS did not look at (`coverage`).
      - `truncated` boolean, required
      - `omittedFindings` integer, required
      - `omittedEvidence` integer, required
      - `contractTruncated` boolean, required

## Other responses

- `401` — Missing, invalid, revoked, or orphaned key (`UNAUTHORIZED`) — or the **target MCP server** needs an OAuth grant (`OAUTH_REQUIRED`), which is a property of the server, not your key.
- `403` — Key is valid but not allowed to do this.
- `404` — Unknown project, server, or resource.
- `429` — Per-key rate limit exceeded (60 requests/minute sustained, bursts up to 10). Honor `Retry-After` and back off with jitter.
- `500` — Something failed on MCPJam's side.

---

[API](https://skmtc.net/mcpjam/apis/mcpjam-api.md) · [All operations](https://skmtc.net/mcpjam/apis/mcpjam-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/mcpjam/mcpjam-api/revisions/d3adfe49fbbf/schema)
