{
  "info": {
    "_postman_id": "63e8d952-4724-4423-b9bc-b02cc75388cf",
    "name": "Moveris Liveness API v2",
    "description": "Complete API collection for Moveris Liveness Detection API v2.\n\n## Authentication\nAll endpoints require an `X-API-Key` header.\n\n## Model Resolution\nTwo modes are supported:\n- **v1 (direct):** Set `model` field in the request body.\n- **v2 (header-based):** Set `X-Model-Version` header (e.g. `latest`) + `frame_count` in the body. The `model` field is ignored.\n\n## Response Headers (v2)\n| Header | Description |\n|--------|-------------|\n| X-Moveris-Model-Resolved | The actual model used |\n| Deprecation | Present if model is deprecated |\n| Sunset | Planned removal date (if deprecated) |\n| X-Moveris-Suggested-Model | Recommended replacement |\n\n## Score Convention\n| Field | Range | Description |\n|-------|-------|-------------|\n| real_score | 0-1 | Raw probability |\n| score | 0-100 | Percentage for display |\n| confidence | 0-1 | Model confidence |\n\n## Verdict Classification\n| real_score | Type | Verdict |\n|------------|------|----------|\n| 0-34 | VERY LOW | fake |\n| 35-49 | LOW | fake |\n| 50-64 | MEDIUM | fake |\n| 65-79 | HIGH | live |\n| 80-100 | VERY HIGH | live |",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://api.moveris.com",
      "type": "string",
      "description": "API base URL (no trailing slash)."
    },
    {
      "key": "api_key",
      "value": "sk-your-api-key-here",
      "type": "string",
      "description": "Your X-API-Key for authentication."
    },
    {
      "key": "session_id",
      "value": "",
      "type": "string",
      "description": "Auto-generated session ID (used by stream tests)."
    },
    {
      "key": "tenant_slug",
      "value": "your-tenant",
      "type": "string",
      "description": "Tenant slug for video-detect endpoints."
    },
    {
      "key": "submission_id",
      "value": "",
      "type": "string",
      "description": "Submission ID returned by video-detect submit."
    }
  ],
  "item": [
    {
      "name": "Health",
      "description": "Health check and root endpoints.",
      "item": [
        {
          "name": "Health Check",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status code is 200', function() {",
                  "    pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/health",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "health"
              ]
            },
            "description": "Returns API status, version, environment, loaded models, and dependency health (DB, Redis, Storage)."
          }
        }
      ]
    },
    {
      "name": "Fast-Check",
      "description": "POST /api/v1/fast-check\n\nBatch liveness detection. Sends all frames in a single request.\n\n**v1 (direct):** Set `model` in the body (e.g. '10', 'mixed-10-v2').\n**v2 (header):** Set `X-Model-Version: latest` header + `frame_count` in body.\n\n**Request body (JSON):**\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| session_id | UUID | Yes | Client-generated unique session ID |\n| model | string | No (default: '10') | Model alias |\n| source | 'media' \\| 'live' | No (default: 'media') | Frame source type |\n| fps | number | No (default: 30.0) | Video FPS (required for hybrid models) |\n| frames | FrameData[] | Yes | Array of frame objects |\n| frame_count | int | Only with v2 header | Frame count for v2 resolution |\n| warnings | string[] | No | Frontend capture warnings |\n\n**FrameData:**\n| Field | Type | Description |\n|-------|------|-------------|\n| index | int (\u22650) | Frame sequence number |\n| timestamp_ms | float (\u22650) | Timestamp in ms from start |\n| pixels | string (base64) | Base64-encoded image (PNG recommended) |\n\n**Response (FastCheckResponse):**\n| Field | Type | Description |\n|-------|------|-------------|\n| verdict | 'live' \\| 'fake' | Liveness verdict |\n| confidence | float (0-1) | Confidence score |\n| real_score | float (0-1) | Raw liveness probability |\n| score | float (0-100) | Percentage score |\n| session_id | UUID | Echo of request session_id |\n| model | string | Resolved model alias |\n| processing_ms | int | Server processing time |\n| frames_processed | int | Frames analyzed |\n| available | bool | Model availability |\n| warning | string? | Warning message |\n| warnings | string[]? | Frontend capture warnings |\n| error | string? | Error message |\n| visual_score | float? | Visual-only score (hybrid only) |\n| physio_extracted | bool? | Physio features extracted (hybrid only) |\n\n**Valid models:** 10, 50, 250, hybrid-v2-10, hybrid-v2-30, hybrid-v2-50, hybrid-v2-60, hybrid-v2-90, hybrid-v2-100, hybrid-v2-125, hybrid-v2-150, hybrid-v2-250, mixed-10, mixed-30, mixed-60, mixed-90, mixed-120, mixed-150, mixed-250, mixed-10-v2, mixed-30-v2, mixed-60-v2, mixed-90-v2, mixed-120-v2",
      "item": [
        {
          "name": "Fast-Check (v1 - model in body)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "string"
              },
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{$guid}}\",\n  \"model\": \"10\",\n  \"source\": \"media\",\n  \"fps\": 30.0,\n  \"frames\": [\n    {\n      \"index\": 0,\n      \"timestamp_ms\": 0,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 1,\n      \"timestamp_ms\": 100,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 2,\n      \"timestamp_ms\": 200,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 3,\n      \"timestamp_ms\": 300,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 4,\n      \"timestamp_ms\": 400,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 5,\n      \"timestamp_ms\": 500,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 6,\n      \"timestamp_ms\": 600,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 7,\n      \"timestamp_ms\": 700,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 8,\n      \"timestamp_ms\": 800,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 9,\n      \"timestamp_ms\": 900,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    }\n  ]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/fast-check",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "fast-check"
              ]
            },
            "description": "v1: Model specified directly in the body `model` field."
          }
        },
        {
          "name": "Fast-Check (v2 - header resolution)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "string"
              },
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              },
              {
                "key": "X-Model-Version",
                "value": "latest",
                "type": "string",
                "description": "v2 model resolution header. Combined with frame_count in the body to resolve the specific model. Values: 'latest', 'v1', 'v2', etc."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{$guid}}\",\n  \"model\": \"10\",\n  \"source\": \"media\",\n  \"fps\": 30.0,\n  \"frames\": [\n    {\n      \"index\": 0,\n      \"timestamp_ms\": 0,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 1,\n      \"timestamp_ms\": 100,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 2,\n      \"timestamp_ms\": 200,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 3,\n      \"timestamp_ms\": 300,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 4,\n      \"timestamp_ms\": 400,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 5,\n      \"timestamp_ms\": 500,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 6,\n      \"timestamp_ms\": 600,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 7,\n      \"timestamp_ms\": 700,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 8,\n      \"timestamp_ms\": 800,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 9,\n      \"timestamp_ms\": 900,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    }\n  ],\n  \"frame_count\": 10\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/fast-check",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "fast-check"
              ]
            },
            "description": "v2: Model resolved via X-Model-Version header + frame_count.\nThe `model` field in the body is ignored when the header is present.\nValid frame_count values: 10, 30, 60, 90, 120."
          }
        },
        {
          "name": "Fast-Check (mixed-10-v2)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "string"
              },
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{$guid}}\",\n  \"model\": \"mixed-10-v2\",\n  \"source\": \"media\",\n  \"fps\": 30.0,\n  \"frames\": [\n    {\n      \"index\": 0,\n      \"timestamp_ms\": 0,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 1,\n      \"timestamp_ms\": 100,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 2,\n      \"timestamp_ms\": 200,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 3,\n      \"timestamp_ms\": 300,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 4,\n      \"timestamp_ms\": 400,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 5,\n      \"timestamp_ms\": 500,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 6,\n      \"timestamp_ms\": 600,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 7,\n      \"timestamp_ms\": 700,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 8,\n      \"timestamp_ms\": 800,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 9,\n      \"timestamp_ms\": 900,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    }\n  ]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/fast-check",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "fast-check"
              ]
            },
            "description": "v1 with mixed-10-v2 model (cross-environment robust)."
          }
        }
      ]
    },
    {
      "name": "Fast-Check Crops",
      "description": "POST /api/v1/fast-check-crops\n\nLiveness detection with pre-cropped 224x224 face images.\nSame response format as fast-check.\n\n**Request body (JSON):**\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| session_id | UUID | Yes | Client-generated unique session ID |\n| model | string | No (default: '10') | Model alias |\n| source | 'media' \\| 'live' | No (default: 'media') | Frame source type |\n| fps | number | No (default: 30.0) | Video FPS |\n| crops | CropData[] | Yes | Array of pre-cropped face images |\n| frame_count | int | Only with v2 header | For v2 resolution |\n| bg_segmentation | bool | No | Whether bg segmentation was applied |\n| warnings | string[] | No | Frontend capture warnings |\n\n**CropData:**\n| Field | Type | Description |\n|-------|------|-------------|\n| index | int (\u22650) | Crop sequence number |\n| pixels | string (base64) | Base64-encoded 224x224 PNG image |",
      "item": [
        {
          "name": "Fast-Check Crops (v1)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "string"
              },
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{$guid}}\",\n  \"model\": \"10\",\n  \"source\": \"media\",\n  \"fps\": 30.0,\n  \"crops\": [\n    {\n      \"index\": 0,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 1,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 2,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 3,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 4,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 5,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 6,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 7,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 8,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 9,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    }\n  ]\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/fast-check-crops",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "fast-check-crops"
              ]
            },
            "description": "v1: Model specified directly in the body."
          }
        },
        {
          "name": "Fast-Check Crops (v2 - header resolution)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "string"
              },
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              },
              {
                "key": "X-Model-Version",
                "value": "latest",
                "type": "string",
                "description": "v2 model resolution header. Combined with frame_count in the body to resolve the specific model. Values: 'latest', 'v1', 'v2', etc."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{$guid}}\",\n  \"model\": \"10\",\n  \"source\": \"media\",\n  \"fps\": 30.0,\n  \"crops\": [\n    {\n      \"index\": 0,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 1,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 2,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 3,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 4,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 5,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 6,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 7,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 8,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    },\n    {\n      \"index\": 9,\n      \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n    }\n  ],\n  \"frame_count\": 10\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/fast-check-crops",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "fast-check-crops"
              ]
            },
            "description": "v2: X-Model-Version header + frame_count."
          }
        }
      ]
    },
    {
      "name": "Fast-Check Stream",
      "description": "POST /api/v1/fast-check-stream\n\nStreaming liveness detection \u2014 sends **one frame per request**.\nFrames are buffered in Redis. When all required frames arrive, processing triggers automatically and returns the result.\n\n**Request body (JSON):**\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| session_id | UUID | Yes | Same session_id for all frames in a session |\n| model | string | No (default: '10') | Model alias |\n| source | 'media' \\| 'live' | No (default: 'media') | Frame source type |\n| frame | FrameData | Yes | Single frame to buffer |\n| frame_count | int | Only with v2 header | For v2 resolution |\n| warnings | string[] | No | Per-frame capture warnings |\n\n**Response (FastCheckStreamResponse):**\n| Field | Type | Description |\n|-------|------|-------------|\n| status | 'buffering' \\| 'complete' | 'buffering' until all frames received |\n| session_id | UUID | Echo of request session_id |\n| frames_received | int | Frames buffered so far |\n| frames_required | int | Total frames needed |\n| ttl_seconds | int | TTL for buffered frames |\n| verdict | 'live' \\| 'fake'? | Only when status='complete' |\n| confidence | float (0-1)? | Only when complete |\n| real_score | float (0-1)? | Only when complete |\n| score | float (0-100)? | Only when complete |\n| model | string? | Resolved model |\n| processing_ms | int? | Only when complete |\n| frames_processed | int? | Only when complete |\n\n**Workflow:** Send frames 0..N-1 with the same `session_id`. Each returns `status: 'buffering'` until the last frame triggers processing.",
      "item": [
        {
          "name": "Stream Frame (v1 - buffering)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "if (!pm.collectionVariables.get('session_id')) {",
                  "    pm.collectionVariables.set('session_id', pm.variables.replaceIn('{{$guid}}'));",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "string"
              },
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"model\": \"10\",\n  \"source\": \"media\",\n  \"frame\": {\n    \"index\": 0,\n    \"timestamp_ms\": 0,\n    \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/fast-check-stream",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "fast-check-stream"
              ]
            },
            "description": "Send a single frame. Returns status='buffering' until all frames arrive.\nIncrement frame.index and frame.timestamp_ms for each subsequent call."
          }
        },
        {
          "name": "Stream Frame (v2 - header resolution)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "string"
              },
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              },
              {
                "key": "X-Model-Version",
                "value": "latest",
                "type": "string",
                "description": "v2 model resolution header. Combined with frame_count in the body to resolve the specific model. Values: 'latest', 'v1', 'v2', etc."
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"session_id\": \"{{session_id}}\",\n  \"model\": \"10\",\n  \"source\": \"media\",\n  \"frame\": {\n    \"index\": 0,\n    \"timestamp_ms\": 0,\n    \"pixels\": \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJgggAAAAAAAAAAAAAAAA==\"\n  },\n  \"frame_count\": 10\n}"
            },
            "url": {
              "raw": "{{base_url}}/api/v1/fast-check-stream",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "fast-check-stream"
              ]
            },
            "description": "v2: X-Model-Version header + frame_count."
          }
        }
      ]
    },
    {
      "name": "Video Detect",
      "description": "Tenant-scoped async video submission for liveness detection.\n\n**Flow:**\n1. `POST /{tenant_slug}/video/detect` \u2192 Upload video, enqueue for processing\n2. `GET /{tenant_slug}/video/detect/{submission_id}` \u2192 Poll status until completed\n\n**Allowed video formats:** mp4, avi, mov, webm, mkv\n**Max file size:** 500MB\n\nThe video is uploaded to Tigris (S3) immediately, and a job is enqueued to the Redis worker queue. The worker downloads the video, extracts frames, runs ML inference, and updates the DB.\n\n**Metadata:** The `metadata` field is validated against the tenant's `video_metadata_schema` (JSON Schema stored on the Organization model).",
      "item": [
        {
          "name": "Submit Video (file upload)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              }
            ],
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "video_file",
                  "type": "file",
                  "src": "",
                  "description": "Video file to analyze. Formats: mp4, avi, mov, webm, mkv. Max 500MB."
                },
                {
                  "key": "model",
                  "value": "mixed-10-v2",
                  "type": "text",
                  "description": "Model alias for detection. Valid: 10, 50, 250, hybrid-v2-10, hybrid-v2-30, hybrid-v2-50, hybrid-v2-60, hybrid-v2-90, hybrid-v2-100, hybrid-v2-125, hybrid-v2-150, hybrid-v2-250, mixed-10, mixed-30, mixed-60, mixed-90, mixed-120, mixed-150, mixed-250, mixed-10-v2, mixed-30-v2, mixed-60-v2, mixed-90-v2, mixed-120-v2"
                },
                {
                  "key": "metadata",
                  "value": "{\"candidate_id\": \"\", \"interview_id\": \"\"}",
                  "type": "text",
                  "description": "JSON-encoded metadata. Validated against the tenant's video_metadata_schema. Fields depend on tenant config."
                }
              ]
            },
            "url": {
              "raw": "{{base_url}}/api/v1/{{tenant_slug}}/video/detect",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "{{tenant_slug}}",
                "video",
                "detect"
              ]
            },
            "description": "Submit a video file for async liveness detection.\n\n**Form fields (multipart/form-data):**\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| video_file | file | One of video_file/video_url | Video file upload |\n| video_url | string | One of video_file/video_url | Presigned URL to video |\n| model | string | No (default: mixed-10-v2) | Model alias |\n| metadata | string (JSON) | No (default: {}) | Tenant-specific metadata |\n\n**Response (201):**\n| Field | Type | Description |\n|-------|------|-------------|\n| submission_id | UUID | Use this to poll status |\n| tenant | string | Tenant slug |\n| status | 'pending' | Always 'pending' on submit |\n| model | string | Model alias |\n| message | string | Info message |\n| tenant_metadata | object | Echo of parsed metadata |\n| created_at | string (ISO 8601) | Creation timestamp |"
          }
        },
        {
          "name": "Submit Video (URL)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              }
            ],
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "video_url",
                  "value": "",
                  "type": "text",
                  "description": "Presigned URL (S3/GCS) to the video file."
                },
                {
                  "key": "model",
                  "value": "mixed-10-v2",
                  "type": "text",
                  "description": "Model alias for detection."
                },
                {
                  "key": "metadata",
                  "value": "{\"candidate_id\": \"\", \"interview_id\": \"\"}",
                  "type": "text",
                  "description": "JSON-encoded tenant metadata."
                }
              ]
            },
            "url": {
              "raw": "{{base_url}}/api/v1/{{tenant_slug}}/video/detect",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "{{tenant_slug}}",
                "video",
                "detect"
              ]
            },
            "description": "Submit a video via presigned URL (alternative to file upload).\nThe API downloads the video from the URL and processes it."
          }
        },
        {
          "name": "Poll Submission Status",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status code is 200', function() {",
                  "    pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "X-API-Key",
                "value": "{{api_key}}",
                "type": "string"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/v1/{{tenant_slug}}/video/detect/{{submission_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "v1",
                "{{tenant_slug}}",
                "video",
                "detect",
                "{{submission_id}}"
              ]
            },
            "description": "Poll the status of a video submission.\n\n**Path parameters:**\n| Param | Type | Description |\n|-------|------|-------------|\n| tenant_slug | string | Tenant identifier |\n| submission_id | UUID | From the submit response |\n\n**Response fields (always present):**\n| Field | Type | Description |\n|-------|------|-------------|\n| submission_id | UUID | Submission identifier |\n| tenant | string | Tenant slug |\n| status | string | 'pending' / 'processing' / 'completed' / 'failed' |\n| model | string | Model alias |\n| tenant_metadata | object | Tenant-specific metadata |\n| created_at | string | Creation timestamp |\n| message | string | Status description |\n\n**Additional fields when status='completed':**\n| Field | Type | Description |\n|-------|------|-------------|\n| verdict | 'live' / 'fake' | Liveness verdict |\n| confidence | float (0-1) | Confidence score |\n| real_score | float (0-1) | Raw probability |\n| score | float (0-100) | Percentage score |\n| frames_processed | int | Frames analyzed |\n| processing_ms | int | Processing time |\n| completed_at | string | Completion timestamp |\n\n**Additional fields when status='failed':**\n| Field | Type | Description |\n|-------|------|-------------|\n| error | string | Error message |\n| completed_at | string | Failure timestamp |"
          }
        }
      ]
    }
  ]
}