{
  "info": {
    "name": "voicescribe API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "description": "Requests and saved examples follow proxy/openapi.yaml (the authoritative contract). Update both when changing the API. Set base_url and a private environment value for api_key before sending. Upload sets call_id from X-Request-Id on success; analysis must be enabled by the operator. Poll manually no more than once every 5 seconds, or use a webhook. DELETE removes files; PUT replaces the risk profile. No requests run automatically. Pricing: your account plan, with no separate analysis fee. Audio retention: 24 hours; transcripts and results: 30 days by default. Full limits, error behavior and webhook signature verification: {{base_url}}/docs. Open a completed-call dispute with POST /v1/calls/{call_id}/dispute using required score (integer 1-100) and comment (10-1000 printable characters), plus optional category; level is rejected. Another POST replaces the dispute and queues review again. Status is pending, resolved, failed or legacy (old level-only correction). Only resolved accepted or partially_accepted results whose original score/category/checked_at still match the current analysis replace risk, with revised=true; stale disputes have stale=true and revised=false; the original assessment remains in dispute.original. Rejected decisions preserve the original risk. Only accepted/partially_accepted final labels and copied transcripts inform similar new calls in this account; complaint text never enters examples. Disputes survive ordinary call retention until deleted; at most 1000 are retained per account. GET /v1/disputes never exposes copied text; DELETE on the dispute or call deletes its example.\n\nWebhook dispute.resolved is queued once per resolution, including rejection, using the existing HMAC-SHA256 signature, persistent state and retry schedule (1, 5, 30, 120, 600 seconds). A replacement dispute may resolve again for the same call; transport delivery can repeat. No complaint or transcript appears in the payload. Example:\n{\n  \"event\": \"dispute.resolved\",\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"decision\": \"partially_accepted\",\n  \"original\": {\n    \"score\": 85,\n    \"level\": \"critical\",\n    \"category\": \"spam_bulk\"\n  },\n  \"risk\": {\n    \"score\": 40,\n    \"level\": \"medium\",\n    \"category\": \"marketing\",\n    \"suspect_party\": \"none\"\n  }\n}\n\nFor analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.\n\nAll calls are inbound. Channel labels Caller/Callee reliably identify the external caller and the provider subscriber answering the call. Without labels roles are inferred from context; the first utterance is usually the callee greeting. Either party may commit fraud, including a fake hotline called by a victim. Summaries name caller and callee. risk.suspect_party and dispute.resolution.suspect_party use caller, callee, both or none; call.done and dispute.resolved include risk.suspect_party. A low level does not clear the model value or change the score; no_speech always uses none.\n\nDispute review never calls the model for a blocked account or when analysis is disabled; it fails with analysis_disabled. The reviewer sees an omitted category as unspecified and levels marked as account-specific thresholds. When the original effective score is at least 51 and original OTP/payment/remote-access flags or explicit object-bearing requests show deception, the final score cannot fall below 51 regardless of level thresholds. Requests must identify an object such as a code, card, account, money transfer or remote access; isolated pay/transfer words do not suffice. The guard preserves the original category, changes accepted to partially_accepted and adds a short explanation. A later reanalysis changing original score/category/checked_at marks the dispute stale=true and leaves revised=false. In the client area only pending dispute rows are polled individually through /cabinet/api/calls/{id}. Legacy corrections are translated in the client area and panel."
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{api_key}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://voicesscribe.com",
      "type": "string"
    },
    {
      "key": "api_key",
      "value": "",
      "type": "string",
      "description": "Set a private local environment value or Vault-backed value. Do not export real credentials."
    },
    {
      "key": "call_id",
      "value": "",
      "type": "string",
      "description": "Set from a successful upload response header or paste a known call ID."
    },
    {
      "key": "external_id",
      "value": "pbx-call-42",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "POST /v1/audio/transcriptions",
      "request": {
        "method": "POST",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/audio/transcriptions",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "audio",
            "transcriptions"
          ]
        },
        "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
        "body": {
          "mode": "formdata",
          "formdata": [
            {
              "key": "file",
              "type": "file",
              "src": "",
              "description": "Choose a local audio file before sending."
            },
            {
              "key": "model",
              "type": "text",
              "value": "whisper-1"
            },
            {
              "key": "response_format",
              "type": "text",
              "value": "json"
            },
            {
              "key": "external_id",
              "type": "text",
              "value": "{{external_id}}",
              "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
            },
            {
              "key": "language",
              "type": "text",
              "value": "en",
              "disabled": true
            },
            {
              "key": "prompt",
              "type": "text",
              "value": "",
              "disabled": true
            },
            {
              "key": "temperature",
              "type": "text",
              "value": "0",
              "disabled": true
            }
          ]
        }
      },
      "response": [
        {
          "name": "200 json",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"text\": \"Please confirm the delivery tomorrow.\"\n}"
        },
        {
          "name": "200 verbose_json",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"task\": \"transcribe\",\n  \"language\": \"en\",\n  \"duration\": 2.3,\n  \"text\": \"Please confirm the delivery tomorrow.\",\n  \"segments\": [\n    {\n      \"id\": 0,\n      \"seek\": 0,\n      \"start\": 0,\n      \"end\": 2.3,\n      \"text\": \"Please confirm the delivery tomorrow.\",\n      \"tokens\": [],\n      \"temperature\": 0,\n      \"avg_logprob\": 0,\n      \"compression_ratio\": 0,\n      \"no_speech_prob\": 0\n    }\n  ]\n}"
        },
        {
          "name": "200 text",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "text/plain"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "Please confirm the delivery tomorrow."
        },
        {
          "name": "200 srt",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "text/plain"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "1\n00:00:00,000 --> 00:00:02,300\nPlease confirm the delivery tomorrow.\n"
        },
        {
          "name": "200 vtt",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "text/plain"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "WEBVTT\n\n00:00:00.000 --> 00:00:02.300\nPlease confirm the delivery tomorrow.\n"
        },
        {
          "name": "400 example",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Invalid external_id: use at most 128 printable characters\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "403 example",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "413 example",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Payload Too Large",
          "code": 413,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"File too large\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "422 duration",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Unprocessable Entity",
          "code": 422,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"audio_too_long: Max duration is 5400 seconds.\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "422 missing_file",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Unprocessable Entity",
          "code": 422,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"detail\": [\n    {\n      \"type\": \"missing\",\n      \"loc\": [\n        \"body\",\n        \"file\"\n      ],\n      \"msg\": \"Field required\",\n      \"input\": null\n    }\n  ]\n}"
        },
        {
          "name": "429 rate",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "429 quota",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Balance exhausted\",\n    \"type\": \"insufficient_quota\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "429 long_call",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            },
            {
              "key": "Retry-After",
              "value": "60"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Another long call is in progress\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "500 timeout",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Internal Server Error",
          "code": 500,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Transcription timed out\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "500 engine",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Internal Server Error",
          "code": 500,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Internal error during transcription\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "502 example",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Bad Gateway",
          "code": 502,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Transcription node is unavailable\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        },
        {
          "name": "503 example",
          "originalRequest": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/audio/transcriptions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "audio",
                "transcriptions"
              ]
            },
            "description": "Accepts audio decodable by ffmpeg. `model` is a compatibility label;\nthe operator selects the actual local model. There is no streaming.\nAnalysis runs separately after success, only if enabled for the account.\nInternal limit headers sent by a client are stripped by the proxy.\n\nChoose a local audio file. Model is a compatibility label. Entire multipart request <=200 MiB by default with analysis or <=30 MiB normally (node file limit 25 MiB normally). The operator can set VSP_LONG_MAX_BODY_MB to an integer from 1 to 200 MiB; invalid values fall back to 200 MiB and are logged at startup. The same configured limit applies to the node file. Analysis duration <=90 minutes. Use a request timeout over 930 seconds for analysis accounts. external_id is a searchable label, not an idempotency key. For analysis accounts, stereo recordings are transcribed by channel: A is the external caller, B is the provider callee. stereo_caller_channel in the risk profile selects left (default) or right for caller A and affects future uploads. Plain transcription text has no role labels; verbose_json segments can add speaker A/B. Call transcript responses add segments [{start, end, speaker, role, text}] with role caller/callee when stereo utterances are available. Speaker segments share transcript retention and deletion.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": "",
                  "description": "Choose a local audio file before sending."
                },
                {
                  "key": "model",
                  "type": "text",
                  "value": "whisper-1"
                },
                {
                  "key": "response_format",
                  "type": "text",
                  "value": "json"
                },
                {
                  "key": "external_id",
                  "type": "text",
                  "value": "{{external_id}}",
                  "description": "Optional, at most 128 printable Unicode characters; only analysis accounts use it."
                },
                {
                  "key": "language",
                  "type": "text",
                  "value": "en",
                  "disabled": true
                },
                {
                  "key": "prompt",
                  "type": "text",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "temperature",
                  "type": "text",
                  "value": "0",
                  "disabled": true
                }
              ]
            }
          },
          "status": "Service Unavailable",
          "code": 503,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "X-Request-Id",
              "value": "0123456789abcdef"
            },
            {
              "key": "Retry-After",
              "value": "600"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Long calls are unavailable on the active node\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": null\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});",
              "if (pm.response.code === 200) {",
              "  pm.test(\"Request ID returned\", function () {",
              "    pm.expect(pm.response.headers.get(\"X-Request-Id\")).to.match(/^[0-9a-f]{16}$/);",
              "  });",
              "  pm.collectionVariables.set(\"call_id\", pm.response.headers.get(\"X-Request-Id\"));",
              "}"
            ]
          }
        }
      ]
    },
    {
      "name": "GET /v1/balance",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/balance",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "balance"
          ]
        },
        "description": "Available to every account, including accounts without call analysis. Uses the collection's Bearer api_key. Returns currency, billing_scheme (prepay or postpay), current stored balance, effective plan (name, included_minutes, monthly_fee, price_per_min), and current calendar month's usage (period, minutes, requests, amount), calculated as in the client area's Usage tab. Postpay also returns the current stored balance. No query parameters. Shares the calls/risk-profile limit of 20 requests/s per key, separate from transcription. Missing, invalid or blocked key: 401; unconfirmed email: 403; rate limit: 429 with Retry-After: 1."
      },
      "response": []
    },
    {
      "name": "GET /v1/calls",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/calls?limit=50&offset=0",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "calls"
          ],
          "query": [
            {
              "key": "external_id",
              "value": "{{external_id}}",
              "disabled": true,
              "description": "Exact match; duplicates are permitted."
            },
            {
              "key": "status",
              "value": "done",
              "disabled": true,
              "description": ""
            },
            {
              "key": "from",
              "value": "2026-11-05T00:00:00Z",
              "disabled": true,
              "description": "RFC3339 inclusive lower bound."
            },
            {
              "key": "to",
              "value": "2026-11-06T00:00:00Z",
              "disabled": true,
              "description": "RFC3339 inclusive upper bound; must not be before from."
            },
            {
              "key": "limit",
              "value": "50",
              "disabled": false,
              "description": "Values over 200 are accepted and clamped to 200."
            },
            {
              "key": "offset",
              "value": "0",
              "disabled": false,
              "description": ""
            }
          ]
        },
        "description": "Returns metadata only. Filters combine with AND. An absent external_id\napplies no filter; an empty external_id selects calls without a label.\nBounds are inclusive and apply to creation time. Offset is applied after\nfiltering; new records can shift pages. Accounts with no jobs get an\nempty list even if analysis is disabled. No usage journal is scanned.\n"
      },
      "response": [
        {
          "name": "200 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls"
              ],
              "query": [
                {
                  "key": "external_id",
                  "value": "{{external_id}}",
                  "disabled": true,
                  "description": "Exact match; duplicates are permitted."
                },
                {
                  "key": "status",
                  "value": "done",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "from",
                  "value": "2026-11-05T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive lower bound."
                },
                {
                  "key": "to",
                  "value": "2026-11-06T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive upper bound; must not be before from."
                },
                {
                  "key": "limit",
                  "value": "50",
                  "disabled": false,
                  "description": "Values over 200 are accepted and clamped to 200."
                },
                {
                  "key": "offset",
                  "value": "0",
                  "disabled": false,
                  "description": ""
                }
              ]
            },
            "description": "Returns metadata only. Filters combine with AND. An absent external_id\napplies no filter; an empty external_id selects calls without a label.\nBounds are inclusive and apply to creation time. Offset is applied after\nfiltering; new records can shift pages. Accounts with no jobs get an\nempty list even if analysis is disabled. No usage journal is scanned.\n"
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"data\": [\n    {\n      \"call_id\": \"0123456789abcdef\",\n      \"external_id\": \"pbx-call-42\",\n      \"status\": \"pending\",\n      \"created_at\": \"2026-11-05T12:00:00Z\"\n    }\n  ],\n  \"has_more\": false\n}"
        },
        {
          "name": "400 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls"
              ],
              "query": [
                {
                  "key": "external_id",
                  "value": "{{external_id}}",
                  "disabled": true,
                  "description": "Exact match; duplicates are permitted."
                },
                {
                  "key": "status",
                  "value": "done",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "from",
                  "value": "2026-11-05T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive lower bound."
                },
                {
                  "key": "to",
                  "value": "2026-11-06T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive upper bound; must not be before from."
                },
                {
                  "key": "limit",
                  "value": "50",
                  "disabled": false,
                  "description": "Values over 200 are accepted and clamped to 200."
                },
                {
                  "key": "offset",
                  "value": "0",
                  "disabled": false,
                  "description": ""
                }
              ]
            },
            "description": "Returns metadata only. Filters combine with AND. An absent external_id\napplies no filter; an empty external_id selects calls without a label.\nBounds are inclusive and apply to creation time. Offset is applied after\nfiltering; new records can shift pages. Accounts with no jobs get an\nempty list even if analysis is disabled. No usage journal is scanned.\n"
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Invalid pagination\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls"
              ],
              "query": [
                {
                  "key": "external_id",
                  "value": "{{external_id}}",
                  "disabled": true,
                  "description": "Exact match; duplicates are permitted."
                },
                {
                  "key": "status",
                  "value": "done",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "from",
                  "value": "2026-11-05T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive lower bound."
                },
                {
                  "key": "to",
                  "value": "2026-11-06T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive upper bound; must not be before from."
                },
                {
                  "key": "limit",
                  "value": "50",
                  "disabled": false,
                  "description": "Values over 200 are accepted and clamped to 200."
                },
                {
                  "key": "offset",
                  "value": "0",
                  "disabled": false,
                  "description": ""
                }
              ]
            },
            "description": "Returns metadata only. Filters combine with AND. An absent external_id\napplies no filter; an empty external_id selects calls without a label.\nBounds are inclusive and apply to creation time. Offset is applied after\nfiltering; new records can shift pages. Accounts with no jobs get an\nempty list even if analysis is disabled. No usage journal is scanned.\n"
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls"
              ],
              "query": [
                {
                  "key": "external_id",
                  "value": "{{external_id}}",
                  "disabled": true,
                  "description": "Exact match; duplicates are permitted."
                },
                {
                  "key": "status",
                  "value": "done",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "from",
                  "value": "2026-11-05T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive lower bound."
                },
                {
                  "key": "to",
                  "value": "2026-11-06T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive upper bound; must not be before from."
                },
                {
                  "key": "limit",
                  "value": "50",
                  "disabled": false,
                  "description": "Values over 200 are accepted and clamped to 200."
                },
                {
                  "key": "offset",
                  "value": "0",
                  "disabled": false,
                  "description": ""
                }
              ]
            },
            "description": "Returns metadata only. Filters combine with AND. An absent external_id\napplies no filter; an empty external_id selects calls without a label.\nBounds are inclusive and apply to creation time. Offset is applied after\nfiltering; new records can shift pages. Accounts with no jobs get an\nempty list even if analysis is disabled. No usage journal is scanned.\n"
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls"
              ],
              "query": [
                {
                  "key": "external_id",
                  "value": "{{external_id}}",
                  "disabled": true,
                  "description": "Exact match; duplicates are permitted."
                },
                {
                  "key": "status",
                  "value": "done",
                  "disabled": true,
                  "description": ""
                },
                {
                  "key": "from",
                  "value": "2026-11-05T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive lower bound."
                },
                {
                  "key": "to",
                  "value": "2026-11-06T00:00:00Z",
                  "disabled": true,
                  "description": "RFC3339 inclusive upper bound; must not be before from."
                },
                {
                  "key": "limit",
                  "value": "50",
                  "disabled": false,
                  "description": "Values over 200 are accepted and clamped to 200."
                },
                {
                  "key": "offset",
                  "value": "0",
                  "disabled": false,
                  "description": ""
                }
              ]
            },
            "description": "Returns metadata only. Filters combine with AND. An absent external_id\napplies no filter; an empty external_id selects calls without a label.\nBounds are inclusive and apply to creation time. Offset is applied after\nfiltering; new records can shift pages. Accounts with no jobs get an\nempty list even if analysis is disabled. No usage journal is scanned.\n"
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "GET /v1/calls/{call_id}",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/calls/{{call_id}}",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "calls",
            "{{call_id}}"
          ],
          "query": [
            {
              "key": "fields",
              "value": "transcript,summary,risk",
              "disabled": true,
              "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
            }
          ]
        },
        "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
      },
      "response": [
        {
          "name": "200 pending",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"pending\",\n  \"created_at\": \"2026-11-05T12:00:00Z\"\n}"
        },
        {
          "name": "200 processing",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"processing\",\n  \"created_at\": \"2026-11-05T12:00:00Z\"\n}"
        },
        {
          "name": "200 failed",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"failed\",\n  \"created_at\": \"2026-11-05T12:00:00Z\",\n  \"error\": {\n    \"code\": \"transcript_unavailable\",\n    \"message\": \"Transcript is unavailable\"\n  }\n}"
        },
        {
          "name": "200 done",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"done\",\n  \"created_at\": \"2026-11-05T12:00:00Z\",\n  \"completed_at\": \"2026-11-05T12:00:25Z\",\n  \"audio\": {\n    \"duration_sec\": 12.3,\n    \"language\": \"en\"\n  },\n  \"transcript\": {\n    \"text\": \"I am calling from your bank. Read me the one-time code immediately.\",\n    \"segments\": [\n      {\n        \"start\": 0.0,\n        \"end\": 3.2,\n        \"speaker\": \"A\",\n        \"role\": \"caller\",\n        \"text\": \"I am calling from your bank. Read me the one-time code immediately.\"\n      }\n    ]\n  },\n  \"summary\": {\n    \"language\": \"en\",\n    \"text\": \"The caller claimed to be from a bank and demanded a one-time code.\"\n  },\n  \"risk\": {\n    \"score\": 92,\n    \"level\": \"critical\",\n    \"category\": \"impersonation_bank\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The caller requested a one-time code.\"\n    ],\n    \"flags\": [\n      \"otp_requested\",\n      \"urgency_pressure\"\n    ],\n    \"revised\": false,\n    \"suspect_party\": \"caller\"\n  }\n}"
        },
        {
          "name": "400 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Invalid fields selection\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "404 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Not Found",
          "code": 404,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call not found\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"not_found\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        },
        {
          "name": "500 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Internal Server Error",
          "code": 500,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Analysis result is unavailable\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": \"analysis_unavailable\"\n  }\n}"
        },
        {
          "name": "200 disputed",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ],
              "query": [
                {
                  "key": "fields",
                  "value": "transcript,summary,risk",
                  "disabled": true,
                  "description": "Comma-separated selection. Omit for all three. Unknown or empty items return 400. Repeated fields parameters are combined."
                }
              ]
            },
            "description": "Pending/processing responses contain metadata only; failed responses\nadd error. Completed responses also contain completed_at and audio.\nfields selects only the three content fields, not metadata. No job\n(ordinary transcription, failed upload, expired or deleted call),\nmalformed ID and another account's call all return 404. A job can be\nbriefly absent just after the synchronous upload returns; retry polling\nafter 5 s. An unavailable transcript for a done job is returned as an\nempty string. Missing result file returns 404; unreadable/invalid result\ndata returns 500 analysis_unavailable.\n If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"done\",\n  \"created_at\": \"2026-11-05T12:00:00Z\",\n  \"completed_at\": \"2026-11-05T12:00:25Z\",\n  \"audio\": {\n    \"duration_sec\": 12.3,\n    \"language\": \"en\"\n  },\n  \"transcript\": {\n    \"text\": \"We are calling to describe an optional service promotion. You can decline without any payment.\",\n    \"segments\": [\n      {\n        \"start\": 0.0,\n        \"end\": 3.2,\n        \"speaker\": \"A\",\n        \"role\": \"caller\",\n        \"text\": \"We are calling to describe an optional service promotion. You can decline without any payment.\"\n      }\n    ]\n  },\n  \"summary\": {\n    \"language\": \"en\",\n    \"text\": \"The caller offered an optional service promotion.\"\n  },\n  \"risk\": {\n    \"score\": 40,\n    \"level\": \"medium\",\n    \"category\": \"marketing\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The caller described an optional service promotion.\"\n    ],\n    \"flags\": [],\n    \"revised\": true,\n    \"suspect_party\": \"none\"\n  },\n  \"dispute\": {\n    \"created_at\": \"2026-11-05T12:01:00Z\",\n    \"status\": \"resolved\",\n    \"client_score\": 20,\n    \"client_level\": \"low\",\n    \"category\": \"service_notification\",\n    \"comment\": \"This is our delivery confirmation.\",\n    \"original\": {\n      \"score\": 85,\n      \"level\": \"critical\",\n      \"category\": \"spam_bulk\"\n    },\n    \"resolution\": {\n      \"decision\": \"partially_accepted\",\n      \"score\": 40,\n      \"level\": \"medium\",\n      \"category\": \"marketing\",\n      \"explanation\": \"The call is a service promotion, with no request for codes, money or remote access.\",\n      \"resolved_at\": \"2026-11-05T12:01:25Z\",\n      \"suspect_party\": \"none\"\n    }\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});",
              "if (pm.response.code === 200 && pm.response.json().status) {",
              "  pm.test(\"Known analysis status\", function () {",
              "    pm.expect(pm.response.json().status).to.be.oneOf([\"pending\", \"processing\", \"done\", \"failed\"]);",
              "  });",
              "}"
            ]
          }
        }
      ]
    },
    {
      "name": "DELETE /v1/calls/{call_id}",
      "request": {
        "method": "DELETE",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/calls/{{call_id}}",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "calls",
            "{{call_id}}"
          ]
        },
        "description": "Removes audio, transcript, result and job; preserves usage/billing\nmetadata. Repeating deletion returns 404. A running analysis cannot\nstore its result afterwards. Webhook attempts not yet started are\nsuppressed; an in-flight attempt may still reach the receiver.\nA filesystem failure may leave partially deleted files; retry deletion.\n Also removes any dispute and its transcript example. A retained example can be deleted together with this call even after the ordinary call files expire."
      },
      "response": [
        {
          "name": "204 Deleted",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ]
            },
            "description": "Removes audio, transcript, result and job; preserves usage/billing\nmetadata. Repeating deletion returns 404. A running analysis cannot\nstore its result afterwards. Webhook attempts not yet started are\nsuppressed; an in-flight attempt may still reach the receiver.\nA filesystem failure may leave partially deleted files; retry deletion.\n"
          },
          "status": "No Content",
          "code": 204,
          "header": [],
          "body": ""
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ]
            },
            "description": "Removes audio, transcript, result and job; preserves usage/billing\nmetadata. Repeating deletion returns 404. A running analysis cannot\nstore its result afterwards. Webhook attempts not yet started are\nsuppressed; an in-flight attempt may still reach the receiver.\nA filesystem failure may leave partially deleted files; retry deletion.\n"
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ]
            },
            "description": "Removes audio, transcript, result and job; preserves usage/billing\nmetadata. Repeating deletion returns 404. A running analysis cannot\nstore its result afterwards. Webhook attempts not yet started are\nsuppressed; an in-flight attempt may still reach the receiver.\nA filesystem failure may leave partially deleted files; retry deletion.\n"
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "404 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ]
            },
            "description": "Removes audio, transcript, result and job; preserves usage/billing\nmetadata. Repeating deletion returns 404. A running analysis cannot\nstore its result afterwards. Webhook attempts not yet started are\nsuppressed; an in-flight attempt may still reach the receiver.\nA filesystem failure may leave partially deleted files; retry deletion.\n"
          },
          "status": "Not Found",
          "code": 404,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call not found\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"not_found\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ]
            },
            "description": "Removes audio, transcript, result and job; preserves usage/billing\nmetadata. Repeating deletion returns 404. A running analysis cannot\nstore its result afterwards. Webhook attempts not yet started are\nsuppressed; an in-flight attempt may still reach the receiver.\nA filesystem failure may leave partially deleted files; retry deletion.\n"
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        },
        {
          "name": "500 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}"
              ]
            },
            "description": "Removes audio, transcript, result and job; preserves usage/billing\nmetadata. Repeating deletion returns 404. A running analysis cannot\nstore its result afterwards. Webhook attempts not yet started are\nsuppressed; an in-flight attempt may still reach the receiver.\nA filesystem failure may leave partially deleted files; retry deletion.\n"
          },
          "status": "Internal Server Error",
          "code": 500,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Could not delete the call\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": \"delete_failed\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(204);",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "GET /v1/calls/{call_id}/transcript",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "calls",
            "{{call_id}}",
            "transcript"
          ]
        },
        "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
      },
      "response": [
        {
          "name": "200 done",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"transcript\": {\n    \"text\": \"Please confirm the delivery tomorrow.\",\n    \"segments\": [\n      {\n        \"start\": 0.0,\n        \"end\": 3.2,\n        \"speaker\": \"A\",\n        \"role\": \"caller\",\n        \"text\": \"Please confirm the delivery tomorrow.\"\n      }\n    ]\n  }\n}"
        },
        {
          "name": "200 pending",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"pending\",\n  \"created_at\": \"2026-11-05T12:00:00Z\"\n}"
        },
        {
          "name": "200 failed",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"failed\",\n  \"created_at\": \"2026-11-05T12:00:00Z\",\n  \"error\": {\n    \"code\": \"transcript_unavailable\",\n    \"message\": \"Transcript is unavailable\"\n  }\n}"
        },
        {
          "name": "400 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Invalid fields selection\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "404 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "Not Found",
          "code": 404,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call not found\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"not_found\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        },
        {
          "name": "500 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/transcript",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "transcript"
              ]
            },
            "description": "Returns stored transcript text and optional stereo segments with start/end in seconds, speaker A (external caller) or B (provider callee), role caller/callee and text. Legacy srt/vtt uploads may retain subtitle markup. A fields query, if supplied, is validated but this path always selects transcript."
          },
          "status": "Internal Server Error",
          "code": 500,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Analysis result is unavailable\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": \"analysis_unavailable\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});",
              "if (pm.response.code === 200 && pm.response.json().status) {",
              "  pm.test(\"Known analysis status\", function () {",
              "    pm.expect(pm.response.json().status).to.be.oneOf([\"pending\", \"processing\", \"done\", \"failed\"]);",
              "  });",
              "}"
            ]
          }
        }
      ]
    },
    {
      "name": "GET /v1/calls/{call_id}/summary",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "calls",
            "{{call_id}}",
            "summary"
          ]
        },
        "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
      },
      "response": [
        {
          "name": "200 done",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"summary\": {\n    \"language\": \"en\",\n    \"text\": \"The caller asked to confirm a delivery for tomorrow. The callee agreed.\"\n  }\n}"
        },
        {
          "name": "200 pending",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"pending\",\n  \"created_at\": \"2026-11-05T12:00:00Z\"\n}"
        },
        {
          "name": "200 failed",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"failed\",\n  \"created_at\": \"2026-11-05T12:00:00Z\",\n  \"error\": {\n    \"code\": \"transcript_unavailable\",\n    \"message\": \"Transcript is unavailable\"\n  }\n}"
        },
        {
          "name": "400 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Invalid fields selection\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "404 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "Not Found",
          "code": 404,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call not found\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"not_found\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        },
        {
          "name": "500 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/summary",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "summary"
              ]
            },
            "description": "The summary contains language and text, using the profile language selected for the analysis. For no speech, text is empty and language still contains that profile language. A fields query is validated but this path always selects summary."
          },
          "status": "Internal Server Error",
          "code": 500,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Analysis result is unavailable\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": \"analysis_unavailable\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});",
              "if (pm.response.code === 200 && pm.response.json().status) {",
              "  pm.test(\"Known analysis status\", function () {",
              "    pm.expect(pm.response.json().status).to.be.oneOf([\"pending\", \"processing\", \"done\", \"failed\"]);",
              "  });",
              "}"
            ]
          }
        }
      ]
    },
    {
      "name": "GET /v1/calls/{call_id}/risk",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "calls",
            "{{call_id}}",
            "risk"
          ]
        },
        "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
      },
      "response": [
        {
          "name": "200 done",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"risk\": {\n    \"score\": 92,\n    \"level\": \"critical\",\n    \"category\": \"impersonation_bank\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The caller requested a one-time code.\"\n    ],\n    \"flags\": [\n      \"otp_requested\",\n      \"urgency_pressure\"\n    ],\n    \"revised\": false,\n    \"suspect_party\": \"caller\"\n  }\n}"
        },
        {
          "name": "200 pending",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"pending\",\n  \"created_at\": \"2026-11-05T12:00:00Z\"\n}"
        },
        {
          "name": "200 failed",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"call_id\": \"0123456789abcdef\",\n  \"external_id\": \"pbx-call-42\",\n  \"status\": \"failed\",\n  \"created_at\": \"2026-11-05T12:00:00Z\",\n  \"error\": {\n    \"code\": \"transcript_unavailable\",\n    \"message\": \"Transcript is unavailable\"\n  }\n}"
        },
        {
          "name": "400 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Invalid fields selection\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "404 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Not Found",
          "code": 404,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call not found\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"not_found\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        },
        {
          "name": "500 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "Internal Server Error",
          "code": 500,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Analysis result is unavailable\",\n    \"type\": \"server_error\",\n    \"param\": null,\n    \"code\": \"analysis_unavailable\"\n  }\n}"
        },
        {
          "name": "200 partially accepted dispute",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"risk\": {\n    \"score\": 40,\n    \"level\": \"medium\",\n    \"category\": \"marketing\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The caller described an optional service promotion.\"\n    ],\n    \"flags\": [],\n    \"revised\": true,\n    \"suspect_party\": \"none\"\n  },\n  \"dispute\": {\n    \"created_at\": \"2026-11-05T12:01:00Z\",\n    \"status\": \"resolved\",\n    \"client_score\": 20,\n    \"client_level\": \"low\",\n    \"category\": \"service_notification\",\n    \"comment\": \"This is our delivery confirmation.\",\n    \"original\": {\n      \"score\": 85,\n      \"level\": \"critical\",\n      \"category\": \"spam_bulk\"\n    },\n    \"resolution\": {\n      \"decision\": \"partially_accepted\",\n      \"score\": 40,\n      \"level\": \"medium\",\n      \"category\": \"marketing\",\n      \"explanation\": \"The call is a service promotion, with no request for codes, money or remote access.\",\n      \"resolved_at\": \"2026-11-05T12:01:25Z\",\n      \"suspect_party\": \"none\"\n    }\n  }\n}"
        },
        {
          "name": "200 rejected dispute keeps original risk",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"risk\": {\n    \"score\": 85,\n    \"level\": \"critical\",\n    \"category\": \"spam_bulk\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The caller made an unsolicited bulk offer.\"\n    ],\n    \"flags\": [],\n    \"revised\": false,\n    \"suspect_party\": \"caller\"\n  },\n  \"dispute\": {\n    \"created_at\": \"2026-11-05T12:01:00Z\",\n    \"status\": \"resolved\",\n    \"client_score\": 20,\n    \"client_level\": \"low\",\n    \"category\": \"service_notification\",\n    \"comment\": \"This is our delivery confirmation.\",\n    \"original\": {\n      \"score\": 85,\n      \"level\": \"critical\",\n      \"category\": \"spam_bulk\"\n    },\n    \"resolution\": {\n      \"decision\": \"rejected\",\n      \"score\": 85,\n      \"level\": \"critical\",\n      \"category\": \"spam_bulk\",\n      \"explanation\": \"The original assessment is supported by the call.\",\n      \"resolved_at\": \"2026-11-05T12:01:25Z\",\n      \"suspect_party\": \"caller\"\n    }\n  }\n}"
        },
        {
          "name": "200 dispute review failed after three attempts",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"risk\": {\n    \"score\": 85,\n    \"level\": \"critical\",\n    \"category\": \"spam_bulk\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The caller made an unsolicited bulk offer.\"\n    ],\n    \"flags\": [],\n    \"revised\": false,\n    \"suspect_party\": \"caller\"\n  },\n  \"dispute\": {\n    \"created_at\": \"2026-11-05T12:01:00Z\",\n    \"status\": \"failed\",\n    \"client_score\": 20,\n    \"client_level\": \"low\",\n    \"category\": \"service_notification\",\n    \"comment\": \"This is our delivery confirmation.\",\n    \"original\": {\n      \"score\": 85,\n      \"level\": \"critical\",\n      \"category\": \"spam_bulk\"\n    },\n    \"error\": \"llm_invalid\"\n  }\n}"
        },
        {
          "name": "200 - stale dispute after reanalysis",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"risk\": {\n    \"score\": 55,\n    \"level\": \"high\",\n    \"category\": \"other_suspicious\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The current analysis found suspicious actions by both parties.\"\n    ],\n    \"flags\": [],\n    \"revised\": false,\n    \"suspect_party\": \"both\"\n  },\n  \"dispute\": {\n    \"created_at\": \"2026-11-05T12:01:00Z\",\n    \"status\": \"resolved\",\n    \"client_score\": 20,\n    \"client_level\": \"low\",\n    \"category\": \"service_notification\",\n    \"comment\": \"This is our delivery confirmation.\",\n    \"original\": {\n      \"score\": 85,\n      \"level\": \"critical\",\n      \"category\": \"spam_bulk\"\n    },\n    \"resolution\": {\n      \"decision\": \"partially_accepted\",\n      \"score\": 40,\n      \"level\": \"medium\",\n      \"category\": \"marketing\",\n      \"explanation\": \"The call is a service promotion, with no request for codes, money or remote access.\",\n      \"resolved_at\": \"2026-11-05T12:01:25Z\",\n      \"suspect_party\": \"none\"\n    },\n    \"stale\": true\n  }\n}"
        },
        {
          "name": "200 - review failed because analysis was disabled",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"risk\": {\n    \"score\": 85,\n    \"level\": \"critical\",\n    \"category\": \"spam_bulk\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The caller made an unsolicited bulk offer.\"\n    ],\n    \"flags\": [],\n    \"revised\": false,\n    \"suspect_party\": \"caller\"\n  },\n  \"dispute\": {\n    \"created_at\": \"2026-11-05T12:01:00Z\",\n    \"status\": \"failed\",\n    \"client_score\": 20,\n    \"client_level\": \"low\",\n    \"category\": \"service_notification\",\n    \"comment\": \"This is our delivery confirmation.\",\n    \"original\": {\n      \"score\": 85,\n      \"level\": \"critical\",\n      \"category\": \"spam_bulk\"\n    },\n    \"error\": \"analysis_disabled\"\n  }\n}"
        },
        {
          "name": "200 - suspicious callee on an inbound call",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"risk\": {\n    \"score\": 92,\n    \"level\": \"critical\",\n    \"category\": \"phishing\",\n    \"suspect_party\": \"callee\",\n    \"confidence\": 0.9,\n    \"reasons\": [\n      \"The callee posing as a hotline requested the caller card details and one-time code.\"\n    ],\n    \"flags\": [\n      \"otp_requested\"\n    ],\n    \"revised\": false\n  }\n}"
        },
        {
          "name": "200 - no speech",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "risk"
              ]
            },
            "description": "Score and level use the current account profile, including for old calls. A fields query is validated but this path always selects risk. If a dispute exists, dispute includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and any resolution {decision, score, level, category, suspect_party, explanation, resolved_at}. risk uses the final accepted or partially_accepted assessment, including suspect_party, and revised=true only while the original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true; pending, failed, rejected and stale disputes preserve the current analysis risk with revised=false. The original confidence is preserved. risk.suspect_party and dispute.resolution.suspect_party are caller, callee, both or none; low risk does not clear this information and no_speech always uses none."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"risk\": {\n    \"score\": 1,\n    \"level\": \"low\",\n    \"category\": \"legit\",\n    \"suspect_party\": \"none\",\n    \"confidence\": 1,\n    \"reasons\": [],\n    \"flags\": [\n      \"no_speech\"\n    ],\n    \"revised\": false\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});",
              "if (pm.response.code === 200 && pm.response.json().status) {",
              "  pm.test(\"Known analysis status\", function () {",
              "    pm.expect(pm.response.json().status).to.be.oneOf([\"pending\", \"processing\", \"done\", \"failed\"]);",
              "  });",
              "}"
            ]
          }
        }
      ]
    },
    {
      "name": "POST /v1/calls/{call_id}/dispute",
      "request": {
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "calls",
            "{{call_id}}",
            "dispute"
          ]
        },
        "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
        "body": {
          "mode": "raw",
          "raw": "{\n  \"score\": 20,\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\"\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        }
      },
      "response": [
        {
          "name": "200 pending review",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score\": 20,\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"client\": \"example-client\",\n  \"call_id\": \"0123456789abcdef\",\n  \"created_at\": \"2026-11-05T12:01:00Z\",\n  \"status\": \"pending\",\n  \"client_score\": 20,\n  \"client_level\": \"low\",\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\",\n  \"original\": {\n    \"score\": 85,\n    \"level\": \"critical\",\n    \"category\": \"spam_bulk\"\n  }\n}"
        },
        {
          "name": "400 missing required score",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"comment\": \"This is our delivery confirmation.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Score 1?100 and a printable comment of 10?1000 characters are required; category must be valid\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "400 complaint too short",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score\": 20,\n  \"comment\": \"Too short\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Score 1?100 and a printable comment of 10?1000 characters are required; category must be valid\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score\": 20,\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 disabled",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score\": 20,\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call analysis is not enabled\",\n    \"type\": \"permission_error\",\n    \"param\": null,\n    \"code\": \"analysis_not_enabled\"\n  }\n}"
        },
        {
          "name": "403 email",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score\": 20,\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score\": 20,\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        },
        {
          "name": "404 example",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score\": 20,\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Not Found",
          "code": 404,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call not found\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"not_found\"\n  }\n}"
        },
        {
          "name": "409 example",
          "originalRequest": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Requires analysis enabled and an owned call with status done. score (integer 1-100) and comment (10-1000 printable Unicode characters explaining your disagreement) are required; category is optional. level and unknown fields are rejected. Replaces the previous dispute and queues review again with status pending. Review shares the serial analyzer queue, Mac pause and unavailable-node policy; record-specific failures allow three attempts, then status failed. The original assessment is preserved. Only resolved accepted or partially_accepted assessments inform similar future calls for this account, using final labels and transcript without the complaint. Up to 1000 recent disputes and up to 2000 transcript characters per dispute are retained until deleted. Responses never include the copied text. Shares the 20 requests/s API-key limit.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score\": 20,\n  \"category\": \"service_notification\",\n  \"comment\": \"This is our delivery confirmation.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Conflict",
          "code": 409,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call analysis is not ready\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"analysis_not_ready\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});",
              "pm.test(\"Correction response excludes transcript text\", function () {",
              "  const body = pm.response.json();",
              "  pm.expect(body).to.have.property(\"call_id\");",
              "  pm.expect(body).to.have.property(\"model\");",
              "  pm.expect(body).not.to.have.property(\"text\");",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "DELETE /v1/calls/{call_id}/dispute",
      "request": {
        "method": "DELETE",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "calls",
            "{{call_id}}",
            "dispute"
          ]
        },
        "description": "Available even after analysis is disabled. Deletes your dispute and its saved transcript example, including after the original call expires. The example no longer informs new analyses. An owned call without a dispute also returns 204; without an owned call or retained dispute the response is 404. Shares the 20 requests/s API-key limit."
      },
      "response": [
        {
          "name": "204 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Available even after analysis is disabled. Deletes your dispute and its saved transcript example, including after the original call expires. The example no longer informs new analyses. An owned call without a dispute also returns 204; without an owned call or retained dispute the response is 404. Shares the 20 requests/s API-key limit."
          },
          "status": "No Content",
          "code": 204,
          "header": [],
          "body": ""
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Available even after analysis is disabled. Deletes your dispute and its saved transcript example, including after the original call expires. The example no longer informs new analyses. An owned call without a dispute also returns 204; without an owned call or retained dispute the response is 404. Shares the 20 requests/s API-key limit."
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 disabled",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Available even after analysis is disabled. Deletes your dispute and its saved transcript example, including after the original call expires. The example no longer informs new analyses. An owned call without a dispute also returns 204; without an owned call or retained dispute the response is 404. Shares the 20 requests/s API-key limit."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call analysis is not enabled\",\n    \"type\": \"permission_error\",\n    \"param\": null,\n    \"code\": \"analysis_not_enabled\"\n  }\n}"
        },
        {
          "name": "403 email",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Available even after analysis is disabled. Deletes your dispute and its saved transcript example, including after the original call expires. The example no longer informs new analyses. An owned call without a dispute also returns 204; without an owned call or retained dispute the response is 404. Shares the 20 requests/s API-key limit."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Available even after analysis is disabled. Deletes your dispute and its saved transcript example, including after the original call expires. The example no longer informs new analyses. An owned call without a dispute also returns 204; without an owned call or retained dispute the response is 404. Shares the 20 requests/s API-key limit."
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        },
        {
          "name": "404 example",
          "originalRequest": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/calls/{{call_id}}/dispute",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "calls",
                "{{call_id}}",
                "dispute"
              ]
            },
            "description": "Available even after analysis is disabled. Deletes your dispute and its saved transcript example, including after the original call expires. The example no longer informs new analyses. An owned call without a dispute also returns 204; without an owned call or retained dispute the response is 404. Shares the 20 requests/s API-key limit."
          },
          "status": "Not Found",
          "code": 404,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call not found\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"not_found\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(204);",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "GET /v1/disputes",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/disputes?limit=50&offset=0",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "disputes"
          ],
          "query": [
            {
              "key": "limit",
              "value": "50"
            },
            {
              "key": "offset",
              "value": "0"
            }
          ]
        },
        "description": "Available even after analysis is disabled. Returns only your disputes, newest first, including disputes retained after calls expire. Includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and resolution {decision, score, level, category, suspect_party, explanation, resolved_at} when resolved. Pending, failed, rejected and stale disputes do not change risk; accepted or partially_accepted resolutions set risk.revised=true only while their original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true. Copied transcript text is never returned. limit defaults to 50 and is capped at 200; offset defaults to 0. Invalid pagination returns 400. Shares the 20 requests/s API-key limit."
      },
      "response": [
        {
          "name": "200 resolved dispute",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/disputes?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "disputes"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50"
                },
                {
                  "key": "offset",
                  "value": "0"
                }
              ]
            },
            "description": "Available even after analysis is disabled. Returns only your disputes, newest first, including disputes retained after calls expire. Includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and resolution {decision, score, level, category, suspect_party, explanation, resolved_at} when resolved. Pending, failed, rejected and stale disputes do not change risk; accepted or partially_accepted resolutions set risk.revised=true only while their original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true. Copied transcript text is never returned. limit defaults to 50 and is capped at 200; offset defaults to 0. Invalid pagination returns 400. Shares the 20 requests/s API-key limit."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"data\": [\n    {\n      \"client\": \"example-client\",\n      \"call_id\": \"0123456789abcdef\",\n      \"created_at\": \"2026-11-05T12:01:00Z\",\n      \"status\": \"resolved\",\n      \"client_score\": 20,\n      \"client_level\": \"low\",\n      \"category\": \"service_notification\",\n      \"comment\": \"This is our delivery confirmation.\",\n      \"original\": {\n        \"score\": 85,\n        \"level\": \"critical\",\n        \"category\": \"spam_bulk\"\n      },\n      \"resolution\": {\n        \"decision\": \"partially_accepted\",\n        \"score\": 40,\n        \"level\": \"medium\",\n        \"category\": \"marketing\",\n        \"explanation\": \"The call is a service promotion, with no request for codes, money or remote access.\",\n        \"resolved_at\": \"2026-11-05T12:01:25Z\",\n        \"suspect_party\": \"none\"\n      }\n    }\n  ],\n  \"has_more\": false\n}"
        },
        {
          "name": "400 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/disputes?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "disputes"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50"
                },
                {
                  "key": "offset",
                  "value": "0"
                }
              ]
            },
            "description": "Available even after analysis is disabled. Returns only your disputes, newest first, including disputes retained after calls expire. Includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and resolution {decision, score, level, category, suspect_party, explanation, resolved_at} when resolved. Pending, failed, rejected and stale disputes do not change risk; accepted or partially_accepted resolutions set risk.revised=true only while their original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true. Copied transcript text is never returned. limit defaults to 50 and is capped at 200; offset defaults to 0. Invalid pagination returns 400. Shares the 20 requests/s API-key limit."
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Invalid pagination\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/disputes?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "disputes"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50"
                },
                {
                  "key": "offset",
                  "value": "0"
                }
              ]
            },
            "description": "Available even after analysis is disabled. Returns only your disputes, newest first, including disputes retained after calls expire. Includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and resolution {decision, score, level, category, suspect_party, explanation, resolved_at} when resolved. Pending, failed, rejected and stale disputes do not change risk; accepted or partially_accepted resolutions set risk.revised=true only while their original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true. Copied transcript text is never returned. limit defaults to 50 and is capped at 200; offset defaults to 0. Invalid pagination returns 400. Shares the 20 requests/s API-key limit."
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 disabled",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/disputes?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "disputes"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50"
                },
                {
                  "key": "offset",
                  "value": "0"
                }
              ]
            },
            "description": "Available even after analysis is disabled. Returns only your disputes, newest first, including disputes retained after calls expire. Includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and resolution {decision, score, level, category, suspect_party, explanation, resolved_at} when resolved. Pending, failed, rejected and stale disputes do not change risk; accepted or partially_accepted resolutions set risk.revised=true only while their original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true. Copied transcript text is never returned. limit defaults to 50 and is capped at 200; offset defaults to 0. Invalid pagination returns 400. Shares the 20 requests/s API-key limit."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call analysis is not enabled\",\n    \"type\": \"permission_error\",\n    \"param\": null,\n    \"code\": \"analysis_not_enabled\"\n  }\n}"
        },
        {
          "name": "403 email",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/disputes?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "disputes"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50"
                },
                {
                  "key": "offset",
                  "value": "0"
                }
              ]
            },
            "description": "Available even after analysis is disabled. Returns only your disputes, newest first, including disputes retained after calls expire. Includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and resolution {decision, score, level, category, suspect_party, explanation, resolved_at} when resolved. Pending, failed, rejected and stale disputes do not change risk; accepted or partially_accepted resolutions set risk.revised=true only while their original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true. Copied transcript text is never returned. limit defaults to 50 and is capped at 200; offset defaults to 0. Invalid pagination returns 400. Shares the 20 requests/s API-key limit."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/disputes?limit=50&offset=0",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "disputes"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "50"
                },
                {
                  "key": "offset",
                  "value": "0"
                }
              ]
            },
            "description": "Available even after analysis is disabled. Returns only your disputes, newest first, including disputes retained after calls expire. Includes status, client_score, client_level, category, comment, created_at, original {score, level, category} and resolution {decision, score, level, category, suspect_party, explanation, resolved_at} when resolved. Pending, failed, rejected and stale disputes do not change risk; accepted or partially_accepted resolutions set risk.revised=true only while their original score/category/checked_at match the current analysis. A changed analysis marks dispute.stale=true. Copied transcript text is never returned. limit defaults to 50 and is capped at 200; offset defaults to 0. Invalid pagination returns 400. Shares the 20 requests/s API-key limit."
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});",
              "pm.test(\"Examples are returned without transcript text\", function () {",
              "  const body = pm.response.json();",
              "  pm.expect(body.data).to.be.an(\"array\");",
              "  pm.expect(body.has_more).to.be.a(\"boolean\");",
              "  body.data.forEach(function (item) { pm.expect(item).not.to.have.property(\"text\"); });",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "GET /v1/risk-profile",
      "request": {
        "method": "GET",
        "header": [],
        "url": {
          "raw": "{{base_url}}/v1/risk-profile",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "risk-profile"
          ]
        },
        "description": "Requires analysis enabled; unlike calls methods, returns 403 when it is disabled."
      },
      "response": [
        {
          "name": "200 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled; unlike calls methods, returns 403 when it is disabled."
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [],\n  \"summary_language\": \"ru\",\n  \"stereo_caller_channel\": \"left\"\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled; unlike calls methods, returns 403 when it is disabled."
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 disabled",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled; unlike calls methods, returns 403 when it is disabled."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call analysis is not enabled\",\n    \"type\": \"permission_error\",\n    \"param\": null,\n    \"code\": \"analysis_not_enabled\"\n  }\n}"
        },
        {
          "name": "403 email",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled; unlike calls methods, returns 403 when it is disabled."
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled; unlike calls methods, returns 403 when it is disabled."
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});"
            ]
          }
        }
      ]
    },
    {
      "name": "PUT /v1/risk-profile",
      "request": {
        "method": "PUT",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/json"
          }
        ],
        "url": {
          "raw": "{{base_url}}/v1/risk-profile",
          "host": [
            "{{base_url}}"
          ],
          "path": [
            "v1",
            "risk-profile"
          ]
        },
        "description": "Requires analysis enabled. JSON body at most 1 MiB, one object, unknown\nfields rejected (including nested thresholds). Require\n1 <= low < medium < high <= 99. Language must be two lowercase ASCII\nletters. normal_categories may be omitted or null to clear it; duplicate\nvalid categories are accepted. Changing the profile does not alter the\nanalysis switch or webhook settings. Historical summaries are not\ntranslated again. The same profile is editable in the operator panel.\n stereo_caller_channel selects left (default when omitted) or right for caller A; the opposite channel is callee B. It affects future uploads.",
        "body": {
          "mode": "raw",
          "raw": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [\n    \"service_notification\",\n    \"marketing\"\n  ],\n  \"summary_language\": \"en\",\n  \"stereo_caller_channel\": \"right\"\n}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        }
      },
      "response": [
        {
          "name": "200 example",
          "originalRequest": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled. JSON body at most 1 MiB, one object, unknown\nfields rejected (including nested thresholds). Require\n1 <= low < medium < high <= 99. Language must be two lowercase ASCII\nletters. normal_categories may be omitted or null to clear it; duplicate\nvalid categories are accepted. Changing the profile does not alter the\nanalysis switch or webhook settings. Historical summaries are not\ntranslated again. The same profile is editable in the operator panel.\n stereo_caller_channel selects left (default when omitted) or right for caller A; the opposite channel is callee B. It affects future uploads.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [\n    \"service_notification\",\n    \"marketing\"\n  ],\n  \"summary_language\": \"en\",\n  \"stereo_caller_channel\": \"right\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "OK",
          "code": 200,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [\n    \"service_notification\",\n    \"marketing\"\n  ],\n  \"summary_language\": \"en\",\n  \"stereo_caller_channel\": \"right\"\n}"
        },
        {
          "name": "400 example",
          "originalRequest": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled. JSON body at most 1 MiB, one object, unknown\nfields rejected (including nested thresholds). Require\n1 <= low < medium < high <= 99. Language must be two lowercase ASCII\nletters. normal_categories may be omitted or null to clear it; duplicate\nvalid categories are accepted. Changing the profile does not alter the\nanalysis switch or webhook settings. Historical summaries are not\ntranslated again. The same profile is editable in the operator panel.\n stereo_caller_channel selects left (default when omitted) or right for caller A; the opposite channel is callee B. It affects future uploads.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [\n    \"service_notification\",\n    \"marketing\"\n  ],\n  \"summary_language\": \"en\",\n  \"stereo_caller_channel\": \"right\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Bad Request",
          "code": 400,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Invalid risk profile\",\n    \"type\": \"invalid_request_error\",\n    \"param\": null,\n    \"code\": \"invalid_request\"\n  }\n}"
        },
        {
          "name": "401 example",
          "originalRequest": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled. JSON body at most 1 MiB, one object, unknown\nfields rejected (including nested thresholds). Require\n1 <= low < medium < high <= 99. Language must be two lowercase ASCII\nletters. normal_categories may be omitted or null to clear it; duplicate\nvalid categories are accepted. Changing the profile does not alter the\nanalysis switch or webhook settings. Historical summaries are not\ntranslated again. The same profile is editable in the operator panel.\n stereo_caller_channel selects left (default when omitted) or right for caller A; the opposite channel is callee B. It affects future uploads.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [\n    \"service_notification\",\n    \"marketing\"\n  ],\n  \"summary_language\": \"en\",\n  \"stereo_caller_channel\": \"right\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Unauthorized",
          "code": 401,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Incorrect API key provided\",\n    \"type\": \"invalid_api_key\",\n    \"param\": null,\n    \"code\": \"invalid_api_key\"\n  }\n}"
        },
        {
          "name": "403 disabled",
          "originalRequest": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled. JSON body at most 1 MiB, one object, unknown\nfields rejected (including nested thresholds). Require\n1 <= low < medium < high <= 99. Language must be two lowercase ASCII\nletters. normal_categories may be omitted or null to clear it; duplicate\nvalid categories are accepted. Changing the profile does not alter the\nanalysis switch or webhook settings. Historical summaries are not\ntranslated again. The same profile is editable in the operator panel.\n stereo_caller_channel selects left (default when omitted) or right for caller A; the opposite channel is callee B. It affects future uploads.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [\n    \"service_notification\",\n    \"marketing\"\n  ],\n  \"summary_language\": \"en\",\n  \"stereo_caller_channel\": \"right\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Call analysis is not enabled\",\n    \"type\": \"permission_error\",\n    \"param\": null,\n    \"code\": \"analysis_not_enabled\"\n  }\n}"
        },
        {
          "name": "403 email",
          "originalRequest": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled. JSON body at most 1 MiB, one object, unknown\nfields rejected (including nested thresholds). Require\n1 <= low < medium < high <= 99. Language must be two lowercase ASCII\nletters. normal_categories may be omitted or null to clear it; duplicate\nvalid categories are accepted. Changing the profile does not alter the\nanalysis switch or webhook settings. Historical summaries are not\ntranslated again. The same profile is editable in the operator panel.\n stereo_caller_channel selects left (default when omitted) or right for caller A; the opposite channel is callee B. It affects future uploads.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [\n    \"service_notification\",\n    \"marketing\"\n  ],\n  \"summary_language\": \"en\",\n  \"stereo_caller_channel\": \"right\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Forbidden",
          "code": 403,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Email address is not confirmed yet. Open the link from the confirmation email.\",\n    \"type\": \"email_not_verified\",\n    \"param\": null,\n    \"code\": \"email_not_verified\"\n  }\n}"
        },
        {
          "name": "429 example",
          "originalRequest": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/risk-profile",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "risk-profile"
              ]
            },
            "description": "Requires analysis enabled. JSON body at most 1 MiB, one object, unknown\nfields rejected (including nested thresholds). Require\n1 <= low < medium < high <= 99. Language must be two lowercase ASCII\nletters. normal_categories may be omitted or null to clear it; duplicate\nvalid categories are accepted. Changing the profile does not alter the\nanalysis switch or webhook settings. Historical summaries are not\ntranslated again. The same profile is editable in the operator panel.\n stereo_caller_channel selects left (default when omitted) or right for caller A; the opposite channel is callee B. It affects future uploads.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"thresholds\": {\n    \"low\": 20,\n    \"medium\": 50,\n    \"high\": 80\n  },\n  \"normal_categories\": [\n    \"service_notification\",\n    \"marketing\"\n  ],\n  \"summary_language\": \"en\",\n  \"stereo_caller_channel\": \"right\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "status": "Too Many Requests",
          "code": 429,
          "header": [
            {
              "key": "Content-Type",
              "value": "application/json"
            },
            {
              "key": "Retry-After",
              "value": "1"
            }
          ],
          "body": "{\n  \"error\": {\n    \"message\": \"Rate limit exceeded\",\n    \"type\": \"rate_limit_exceeded\",\n    \"param\": null,\n    \"code\": \"rate_limit_exceeded\"\n  }\n}"
        }
      ],
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test(\"Expected successful status\", function () {",
              "  pm.expect(pm.response.code).to.eql(200);",
              "});"
            ]
          }
        }
      ]
    }
  ]
}
