---
title: "Match the captures that name no feature to the features they show."
method: POST
path: "/story-projects/{projectId}/captures/match-features"
tags: ["Sources"]
---

# Match the captures that name no feature to the features they show.

`POST /story-projects/{projectId}/captures/match-features`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/story-projects/{projectId}/captures/match-features`
- Auth: HTTP bearer

A recording counts toward a feature only when it names that feature. A recording that names none is matched from its own name, the page it was recorded on and the tour it ran against each feature's name, pages and tours: a strong match is tagged by itself (marked `featureSource: "matched"` until the writer confirms it), and any other likely match is offered as `suggestedFeature` for the writer to confirm or correct. Every capture that names no feature and ran no stored tour is matched, newest first: its `match` (feature, confidence and the reason in plain words) and whether it is `strong`. A dry run unless `{ apply: true }`, which tags the strong matches (`featureSource: "matched"`) and leaves the rest for the writer; a capture already named is never changed. A tagged capture is never matched again, so no Idempotency-Key. Free.

## Request body

- AppCaptureMatchFeatures
  - `apply` boolean: True tags the strong matches; left out or false is a dry run.

## Response `200`

Each untagged capture with its match, and how many were (or would be) tagged.

- AppCaptureFeatureMatchList
  - `projectId` string, required
  - `applied` boolean, required: Whether this call tagged the strong matches or only reported them.
  - `taggedCount` integer, required: How many captures were (or would be) tagged: the strong matches.
  - `leftCount` integer, required: How many are left for the writer: a likely or weak match to confirm, or none.
  - `captures` object[], required
    - `captureId` string, required
    - `label` string, nullable, required
    - `role` 'raw' | 'edit', required
    - `match` AppCaptureFeatureSuggestion, required: The feature a capture most likely shows, matched from its name, page and tour against each feature's name, pages and tours.
      - `featureId` string, required
      - `featureName` string, required
      - `confidence` 'high' | 'medium' | 'low', required: `high` is a strong match (tagged by itself); `medium` a likely one (offered to the writer); `low` a weak guess.
      - `reason` string, required: In plain words: what in the recording names the feature.
    - `strong` boolean, required: A strong match: tagged by this call, or would be in a dry run.

## Other responses

- `400`: Request payload failed validation. The error message names the offending field.
- `401`: Missing, malformed, revoked, or expired API key.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `404`: The named project or source does not exist or is owned by another account.
- `429`: Rate limit for this API key + IP + route family was exceeded.
- `500`: The API failed unexpectedly. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api.md) · [All operations](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/revisions/64c1102ccb2f?raw)
