ThinkRun

llms.txt

Recordings

Submit browser session recordings for AI analysis with frame-level visual descriptions, OCR, and transcription.

POST/api/feedback/recordings

Create a recording document (metadata, actions, browser state). Do not embed video in this request. If the response includes presigned fields (uploadUrl, uploadToken, presignedVideoObjectId, presignedVideoObjectKey, presignedVideoContentType), upload bytes with a browser PUT to uploadUrl using Content-Type exactly matching presignedVideoContentType (and any headers your deployment requires for the upload token). If those fields are omitted (local/dev or presign unavailable), upload the file with PUT /api/feedback/recordings/:id/video instead. Optional videoContentType must match the Content-Type of the direct PUT to the presigned URL.

Body Parameters

metadataobjectRequired fields include version, duration, url, title, intent, features, visitedUrls
actionsarrayRecorded user actions
browserStateobjectconsoleLogs, networkRequests
elementsarrayOptional DOM element snapshots
videoContentTypestringOptional: video/webm (default), video/mp4, or video/quicktime — must match direct upload PUT

Response

{
  "success": true,
  "recordingId": "550e8400-e29b-41d4-a716-446655440000",
  "videoUploadStatus": "pending",
  "status": "pending",
  "uploadUrl": "https://…",
  "uploadToken": "…",
  "presignedVideoObjectId": "obj-…",
  "presignedVideoObjectKey": "app/…/recording_<id>.webm",
  "presignedVideoContentType": "video/webm",
  "presignedTtlSeconds": 300
}
PUT/api/feedback/recordings/:id/video

Server-mediated upload: send the recording video as the raw request body (streaming). Requires Content-Type (video/* or application/octet-stream) and Content-Length. Use this when POST create did not return presigned upload fields, or as a fallback if direct-to-storage upload is not available. When presigned fields are present, prefer the direct PUT to uploadUrl described on POST /api/feedback/recordings.

Body Parameters

(body)binaryrequiredRaw WebM/MP4 bytes

Response

{
  "success": true,
  "recordingId": "550e8400-e29b-41d4-a716-446655440000",
  "videoArtifactId": "obj-123",
  "videoUrl": "https://..."
}
GET/api/feedback/recordings

List all recordings for the authenticated user.

Response

{
  "recordings": [
    {
      "id": "rec-abc123",
      "status": "analyzed",
      "createdAt": "2026-03-01T10:00:00Z",
      "duration": 120
    }
  ]
}
GET/api/feedback/recordings/:id

Get detail for a specific recording.

Response

{
  "id": "rec-abc123",
  "status": "analyzed",
  "sessionId": "session-abc",
  "createdAt": "2026-03-01T10:00:00Z",
  "duration": 120,
  "metadata": {}
}
GET/api/feedback/recordings/:id/video

Get a presigned video download URL for a recording.

Response

{
  "videoUrl": "https://storage.example.com/recordings/rec-abc123.webm?token=..."
}
POST/api/feedback/recordings/:id/analyze

Trigger AI analysis of a recording. Uses mech-media for per-frame visual analysis, OCR, and Whisper transcription.

Response

{
  "status": "analyzing",
  "jobId": "job-xyz"
}
GET/api/feedback/recordings/:id/analysis

Get the AI analysis results for a recording.

Response

{
  "sections": [
    {
      "title": "User navigated to pricing page",
      "timestamp": 15.2,
      "description": "Clicked pricing link in nav...",
      "citations": [
        { "type": "console_error", "timestamp": 15.5 }
      ]
    }
  ]
}
GET/api/feedback/recordings/:id/summary

Get an AI-generated summary of the recording.

Response

{
  "summary": "User browsed the pricing page and attempted checkout...",
  "keyFindings": ["Found pricing inconsistency", "Checkout form validation error"]
}
POST/api/feedback/recordings/:id/claim

Claim an unclaimed recording for the authenticated user.

Response

{
  "success": true,
  "id": "rec-abc123",
  "userId": "user-xyz"
}
DELETE/api/feedback/recordings/:id

Delete a recording and its associated analysis.

Response

204 No Content