{
  "openapi": "3.1.0",
  "info": {
    "title": "Kalemio Transcription API",
    "version": "1.0.0",
    "description": "Asynchronous Arabic transcription with word timestamps, speaker labels, JSON results, and SRT subtitles. Uploads are authenticated with an API key. An OpenAI-compatible endpoint, POST /v1/audio/transcriptions, answers in the same request; point an OpenAI SDK at https://kalemio.app/v1. Prepaid accounts are billed by recorded duration rounded up to the next second; usage-based accounts are billed at US$0.015 per processed audio minute, rounded up to whole minutes with a minimum of 2 minutes (US$0.03) per job.",
    "termsOfService": "https://kalemio.app/terms/",
    "contact": {
      "email": "support@kalemio.app",
      "url": "https://kalemio.app/support/"
    }
  },
  "servers": [
    {
      "url": "https://kalemio.app",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Human-readable API documentation",
    "url": "https://kalemio.app/developers/docs/"
  },
  "tags": [
    {
      "name": "Account",
      "description": "Inspect the authenticated account's API balance."
    },
    {
      "name": "Transcriptions",
      "description": "Submit audio or video and retrieve its transcription."
    },
    {
      "name": "OpenAI-compatible",
      "description": "Endpoints shaped after OpenAI's audio transcription API, so the official openai SDKs work with base_url https://kalemio.app/v1 and a Kalemio key. Jobs share the plans, limits and billing of POST /v1/transcriptions."
    }
  ],
  "security": [
    {
      "apiKeyAuth": []
    }
  ],
  "paths": {
    "/v1/account": {
      "get": {
        "operationId": "getAccount",
        "summary": "Get account balance",
        "description": "Returns the account mode and available transcription credit. Values are expressed in seconds. Requires the `usage:read` key permission.",
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "description": "Account balance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/transcriptions": {
      "post": {
        "operationId": "createTranscription",
        "summary": "Submit audio or video",
        "description": "Queues one Arabic audio or video recording for transcription. The request is asynchronous: save the returned job ID and poll it until it is completed or failed. The same Idempotency-Key may be retried for the same file and speaker settings. Requires the `transcriptions:write` key permission.",
        "tags": [
          "Transcriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TranscriptionUpload"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Transcription accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscriptionJob"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Not enough available credit, or the Free plan minutes for this month are used up (code free_quota_exhausted)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Add minutes before submitting this recording."
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The Idempotency-Key was already used for different upload data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Upload exceeds the 20 MiB limit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "The request is not multipart form data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/transcriptions/{id}": {
      "get": {
        "operationId": "getTranscription",
        "summary": "Get transcription status",
        "description": "Returns the current state and billing details for a transcription created by the authenticated account. Requires the `transcriptions:read` key permission.",
        "tags": [
          "Transcriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TranscriptionId"
          }
        ],
        "responses": {
          "200": {
            "description": "Transcription status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscriptionJob"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/transcriptions/{id}/result": {
      "get": {
        "operationId": "getTranscriptionResult",
        "summary": "Get a JSON transcription result",
        "description": "Returns the completed transcription with full text, word-level timestamps, and speaker IDs. Results expire 24 hours after submission. Requires the `transcriptions:read` key permission.",
        "tags": [
          "Transcriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TranscriptionId"
          }
        ],
        "responses": {
          "200": {
            "description": "Completed transcription",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscriptionResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The transcription is not completed yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "The result is not ready."
                }
              }
            }
          },
          "410": {
            "description": "The result has expired or is no longer available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/transcriptions/{id}/srt": {
      "get": {
        "operationId": "getTranscriptionSrt",
        "summary": "Download SRT subtitles",
        "description": "Returns the completed transcription as an SRT subtitle file. The result expires 24 hours after submission. Requires the `transcriptions:read` key permission.",
        "tags": [
          "Transcriptions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/TranscriptionId"
          }
        ],
        "responses": {
          "200": {
            "description": "SRT subtitle file",
            "headers": {
              "Content-Disposition": {
                "description": "Suggested filename for the downloaded subtitle file",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/x-subrip": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The transcription is not completed yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The result has expired or is no longer available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/audio/transcriptions": {
      "post": {
        "operationId": "createOpenAiTranscription",
        "summary": "Transcribe audio (OpenAI-compatible)",
        "description": "Transcribes one Arabic recording and returns the result in the same response, in the shape of OpenAI's create-transcription endpoint. The request waits up to 100 seconds; a job still processing then gets 504 with its id, and sending the identical request again waits on the same job without a second charge. Without an Idempotency-Key, an identical upload (same bytes and speakers setting) that is still processing or finished within the hour is reused. Requires the `transcriptions:write` key permission. Unsupported OpenAI options are rejected with 400 rather than ignored.",
        "tags": [
          "OpenAI-compatible"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/OptionalIdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/OpenAiTranscriptionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The transcription, in the requested response_format",
            "headers": {
              "X-Kalemio-Transcription-Id": {
                "description": "The Kalemio job behind this response. Use it with GET /v1/transcriptions/{id}.",
                "schema": {
                  "type": "string",
                  "pattern": "^tr_[a-f0-9]{32}$"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/OpenAiTranscription"
                    },
                    {
                      "$ref": "#/components/schemas/OpenAiTranscriptionVerbose"
                    },
                    {
                      "$ref": "#/components/schemas/OpenAiTranscriptionDiarized"
                    }
                  ]
                }
              },
              "text/plain": {
                "schema": {
                  "type": "string",
                  "description": "response_format text, srt or vtt."
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "stream=true: transcript.text.delta events (and transcript.text.segment events for diarized_json), then transcript.text.done. Sent once the recording is transcribed, not progressively."
                }
              }
            }
          },
          "400": {
            "description": "Invalid or unsupported parameter, or unreadable media (code, for example, unsupported_language, unsupported_parameter, invalid_file_format, audio_too_long)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, invalid or revoked (code invalid_api_key)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "403": {
            "description": "The API key lacks the transcriptions:write permission",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "404": {
            "description": "model is not a Kalemio model id (code model_not_found). OpenAI model names are not accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "409": {
            "description": "The Idempotency-Key belongs to a different upload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "410": {
            "description": "The result of the reused job has expired",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "413": {
            "description": "The file exceeds 20 MiB (code file_too_large)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "415": {
            "description": "The request is not multipart form data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (rate_limit_exceeded, or a free_* code), or out of minutes (insufficient_quota, sent with x-should-retry: false)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds until the rate-limit window resets, when supplied",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          },
          "500": {
            "description": "The transcription failed (code transcription_failed). Reserved prepaid minutes are returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "503": {
            "description": "The API is temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "504": {
            "description": "Still processing at the deadline (code transcription_pending). error.transcription_id names the job.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            },
            "headers": {
              "X-Kalemio-Transcription-Id": {
                "description": "The Kalemio job behind this response. Use it with GET /v1/transcriptions/{id}.",
                "schema": {
                  "type": "string",
                  "pattern": "^tr_[a-f0-9]{32}$"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before sending the same request again",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          }
        }
      }
    },
    "/v1/models": {
      "get": {
        "operationId": "listModels",
        "summary": "List models (OpenAI-compatible)",
        "description": "Lists the model ids POST /v1/audio/transcriptions accepts. No key is needed; a key that is sent must be valid.",
        "tags": [
          "OpenAI-compatible"
        ],
        "security": [
          {},
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Model list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                }
              }
            }
          },
          "401": {
            "description": "A key was sent and it is invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/models/{model}": {
      "get": {
        "operationId": "retrieveModel",
        "summary": "Retrieve a model (OpenAI-compatible)",
        "tags": [
          "OpenAI-compatible"
        ],
        "security": [
          {},
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "model",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "kalemio-arabic-1"
          }
        ],
        "responses": {
          "200": {
            "description": "The model",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Model"
                }
              }
            }
          },
          "401": {
            "description": "A key was sent and it is invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          },
          "404": {
            "description": "No such model (code model_not_found)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenAIError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "kal_live_<64 hex characters>",
        "description": "Create a scoped key in your Kalemio account, then send it as `Authorization: Bearer kal_live_...`. Keep keys on your server. Rotate lost or exposed keys; the prior key is revoked immediately."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "A stable identifier for this upload attempt. Use 8–128 letters, digits, dots, colons, underscores, or hyphens. Reuse it only for the same file and speaker settings.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9_.:-]+$"
        },
        "example": "recording-2026-001"
      },
      "TranscriptionId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The transcription job ID returned by the upload request.",
        "schema": {
          "type": "string",
          "pattern": "^tr_[a-f0-9]{32}$"
        },
        "example": "tr_0123456789abcdef0123456789abcdef"
      },
      "OptionalIdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional here. The same key with the same file and speakers setting returns the same job without a second charge. 8–128 letters, digits, dots, colons, underscores, or hyphens.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9_.:-]+$"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is invalid",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The API key is missing, invalid, or revoked",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "A valid API key is required."
            }
          }
        }
      },
      "Forbidden": {
        "description": "The API key does not have the permission required by this operation",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "The transcription does not exist for this account",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests. Retry after a short delay.",
        "headers": {
          "Retry-After": {
            "description": "Number of seconds to wait before retrying, when supplied",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "The API is temporarily unavailable or transcription has not launched",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "The media or form fields are invalid",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "Language must be ar; speakers must be auto or 1–20."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message."
          },
          "code": {
            "type": "string",
            "description": "Machine-readable reason, when one applies: free_quota_exhausted (402), invalid_file_format or audio_too_long (422), free_concurrency_limit, free_rate_limited or free_queue_full (429)."
          }
        },
        "additionalProperties": false
      },
      "Account": {
        "type": "object",
        "required": [
          "mode",
          "available_seconds",
          "monthly_seconds",
          "topup_seconds",
          "billing_method",
          "metered_price_usd_per_minute",
          "transcription_enabled",
          "processing",
          "free_plan"
        ],
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "available_seconds": {
            "type": "integer",
            "description": "Total credit currently available, in seconds."
          },
          "monthly_seconds": {
            "type": "integer",
            "description": "Available seconds from the current monthly allowance."
          },
          "topup_seconds": {
            "type": "integer",
            "description": "Available seconds from non-expiring top-ups."
          },
          "billing_method": {
            "type": "string",
            "enum": [
              "credits",
              "metered_api_v1",
              "free"
            ],
            "description": "The billing path used by new transcription jobs."
          },
          "metered_price_usd_per_minute": {
            "type": [
              "number",
              "null"
            ],
            "description": "The usage-based price in US dollars per processed minute, as Stripe charges it, when billing_method is metered_api_v1."
          },
          "transcription_enabled": {
            "type": "boolean",
            "description": "Whether the transcription backend is currently enabled."
          },
          "processing": {
            "type": "string",
            "enum": [
              "standard",
              "priority"
            ],
            "description": "standard: Free plan jobs, which use slower, queued processing on a best-effort basis and are typically several times slower than paid plans. priority: paid plans, with priority, faster processing."
          },
          "free_plan": {
            "anyOf": [
              {
                "type": "object",
                "required": [
                  "minutes_included",
                  "minutes_used",
                  "minutes_remaining",
                  "resets_at",
                  "processing"
                ],
                "properties": {
                  "minutes_included": {
                    "type": "integer",
                    "description": "Free minutes per calendar month (UTC)."
                  },
                  "minutes_used": {
                    "type": "integer",
                    "description": "Whole minutes counted this month, rounded up per job with a minimum of one."
                  },
                  "minutes_remaining": {
                    "type": "integer"
                  },
                  "resets_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "processing": {
                    "type": "string",
                    "const": "standard"
                  }
                },
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "The Free plan allowance this month, or null on a paid plan."
          }
        },
        "additionalProperties": false
      },
      "TranscriptionUpload": {
        "type": "object",
        "required": [
          "file"
        ],
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "description": "One MP3, M4A, PCM WAV, MP4, or MOV file, up to 20 MiB and 10 minutes."
          },
          "language": {
            "type": "string",
            "enum": [
              "ar"
            ],
            "default": "ar",
            "description": "The recording language. Arabic is currently supported."
          },
          "speakers": {
            "type": "string",
            "pattern": "^(auto|[1-9]|1[0-9]|20)$",
            "default": "auto",
            "description": "Use automatic speaker detection, or provide a known speaker count from 1 to 20."
          }
        },
        "additionalProperties": false
      },
      "TranscriptionJob": {
        "type": "object",
        "required": [
          "id",
          "status",
          "duration_seconds",
          "billed_seconds",
          "billed_minutes",
          "reserved_seconds",
          "billing_method",
          "expires_at",
          "processing"
        ],
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^tr_[a-f0-9]{32}$"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "completed",
              "failed"
            ]
          },
          "duration_seconds": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Measured media duration before billing rounding."
          },
          "billed_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Seconds charged after a successful transcription for prepaid credit accounts; zero for metered accounts."
          },
          "billed_minutes": {
            "type": "integer",
            "minimum": 0,
            "description": "Whole minutes counted after a successful transcription for usage-based and Free plan jobs, rounded up. Usage-based jobs are billed for at least 2 minutes; Free plan jobs count at least one minute."
          },
          "reserved_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Prepaid seconds reserved while the job is active; zero for metered accounts."
          },
          "billing_method": {
            "type": "string",
            "enum": [
              "credits",
              "metered_api_v1",
              "free"
            ],
            "description": "The billing path used for this job."
          },
          "error": {
            "type": "string",
            "description": "Present when status is failed. Failed jobs return reserved credit."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Result retention deadline."
          },
          "processing": {
            "type": "string",
            "enum": [
              "standard",
              "priority"
            ],
            "description": "standard: Free plan jobs, which use slower, queued processing on a best-effort basis and are typically several times slower than paid plans. priority: paid plans, with priority, faster processing."
          }
        },
        "additionalProperties": false
      },
      "Word": {
        "type": "object",
        "required": [
          "text",
          "start",
          "end",
          "speaker_id"
        ],
        "properties": {
          "text": {
            "type": "string"
          },
          "start": {
            "type": "number",
            "minimum": 0,
            "description": "Word start time in seconds."
          },
          "end": {
            "type": "number",
            "minimum": 0,
            "description": "Word end time in seconds."
          },
          "speaker_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Speaker label within this recording, or null when unavailable."
          }
        },
        "additionalProperties": false
      },
      "TranscriptionResult": {
        "type": "object",
        "required": [
          "text",
          "words",
          "duration_seconds"
        ],
        "properties": {
          "text": {
            "type": "string",
            "description": "The complete transcript assembled from the word results."
          },
          "words": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Word"
            }
          },
          "duration_seconds": {
            "type": "number",
            "minimum": 0
          }
        },
        "additionalProperties": false
      },
      "OpenAIError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "message",
              "type",
              "param",
              "code"
            ],
            "properties": {
              "message": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "description": "invalid_request_error, insufficient_quota, requests or server_error."
              },
              "param": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "code": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "transcription_id": {
                "type": "string",
                "description": "Present on transcription_pending and transcription_failed."
              }
            }
          }
        }
      },
      "OpenAiTranscriptionRequest": {
        "type": "object",
        "required": [
          "file",
          "model"
        ],
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "description": "One MP3, M4A, PCM WAV, MP4, or MOV file, up to 20 MiB and 10 minutes. FLAC, OGG and WebM are not accepted."
          },
          "model": {
            "type": "string",
            "enum": [
              "kalemio-arabic-1"
            ]
          },
          "language": {
            "type": "string",
            "enum": [
              "ar"
            ],
            "description": "Optional. Arabic only; dialects need no separate hint."
          },
          "response_format": {
            "type": "string",
            "enum": [
              "json",
              "text",
              "srt",
              "vtt",
              "verbose_json",
              "diarized_json"
            ],
            "default": "json"
          },
          "timestamp_granularities[]": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "word",
                "segment"
              ]
            },
            "description": "verbose_json only. Defaults to segment."
          },
          "stream": {
            "type": "boolean",
            "default": false,
            "description": "With json, text or diarized_json. Events are sent once the recording is transcribed."
          },
          "temperature": {
            "type": "number",
            "enum": [
              0
            ],
            "description": "Only 0 (the default) is accepted."
          },
          "chunking_strategy": {
            "type": "string",
            "enum": [
              "auto"
            ]
          },
          "speakers": {
            "type": "string",
            "pattern": "^(auto|[1-9]|1[0-9]|20)$",
            "default": "auto",
            "description": "Kalemio extension: automatic speaker detection or a known count from 1 to 20."
          }
        },
        "description": "prompt (when not empty), keywords[], include[], known_speaker_names[], known_speaker_references[], non-Arabic language values, other chunking strategies and unknown fields are rejected with 400."
      },
      "OpenAiTranscription": {
        "type": "object",
        "required": [
          "text",
          "usage"
        ],
        "properties": {
          "text": {
            "type": "string"
          },
          "usage": {
            "type": "object",
            "required": [
              "type",
              "seconds"
            ],
            "properties": {
              "type": {
                "type": "string",
                "const": "duration"
              },
              "seconds": {
                "type": "integer",
                "description": "Recording duration in seconds, rounded up."
              }
            }
          }
        }
      },
      "OpenAiWord": {
        "type": "object",
        "required": [
          "word",
          "start",
          "end"
        ],
        "properties": {
          "word": {
            "type": "string"
          },
          "start": {
            "type": "number"
          },
          "end": {
            "type": "number"
          }
        }
      },
      "OpenAiSegment": {
        "type": "object",
        "required": [
          "id",
          "seek",
          "start",
          "end",
          "text",
          "tokens",
          "temperature",
          "avg_logprob",
          "compression_ratio",
          "no_speech_prob"
        ],
        "description": "seek, tokens, temperature, avg_logprob, compression_ratio and no_speech_prob are fixed placeholders (0 or []) kept for schema compatibility; Kalemio does not compute them.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "seek": {
            "type": "integer"
          },
          "start": {
            "type": "number"
          },
          "end": {
            "type": "number"
          },
          "text": {
            "type": "string"
          },
          "tokens": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "temperature": {
            "type": "number"
          },
          "avg_logprob": {
            "type": "number"
          },
          "compression_ratio": {
            "type": "number"
          },
          "no_speech_prob": {
            "type": "number"
          }
        }
      },
      "OpenAiTranscriptionVerbose": {
        "type": "object",
        "required": [
          "task",
          "language",
          "duration",
          "text",
          "usage"
        ],
        "properties": {
          "task": {
            "type": "string",
            "const": "transcribe"
          },
          "language": {
            "type": "string",
            "const": "arabic"
          },
          "duration": {
            "type": "number"
          },
          "text": {
            "type": "string"
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpenAiSegment"
            }
          },
          "words": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpenAiWord"
            }
          },
          "usage": {
            "type": "object",
            "required": [
              "type",
              "seconds"
            ],
            "properties": {
              "type": {
                "type": "string",
                "const": "duration"
              },
              "seconds": {
                "type": "integer",
                "description": "Recording duration in seconds, rounded up."
              }
            }
          }
        }
      },
      "OpenAiDiarizedSegment": {
        "type": "object",
        "required": [
          "type",
          "id",
          "start",
          "end",
          "text",
          "speaker"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "transcript.text.segment"
          },
          "id": {
            "type": "string"
          },
          "start": {
            "type": "number"
          },
          "end": {
            "type": "number"
          },
          "text": {
            "type": "string"
          },
          "speaker": {
            "type": "string",
            "description": "A, B, C… in order of first appearance within this recording."
          }
        }
      },
      "OpenAiTranscriptionDiarized": {
        "type": "object",
        "required": [
          "task",
          "duration",
          "text",
          "segments",
          "usage"
        ],
        "properties": {
          "task": {
            "type": "string",
            "const": "transcribe"
          },
          "duration": {
            "type": "number"
          },
          "text": {
            "type": "string"
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpenAiDiarizedSegment"
            }
          },
          "usage": {
            "type": "object",
            "required": [
              "type",
              "seconds"
            ],
            "properties": {
              "type": {
                "type": "string",
                "const": "duration"
              },
              "seconds": {
                "type": "integer",
                "description": "Recording duration in seconds, rounded up."
              }
            }
          }
        }
      },
      "Model": {
        "type": "object",
        "required": [
          "id",
          "object",
          "created",
          "owned_by"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "model"
          },
          "created": {
            "type": "integer"
          },
          "owned_by": {
            "type": "string"
          }
        }
      },
      "ModelList": {
        "type": "object",
        "required": [
          "object",
          "data"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Model"
            }
          }
        }
      }
    }
  }
}
