openapi: 3.1.0
info:
  title: AMBIE API
  description: |
    Audio transcription, intelligence, text translation, TTS, sentiment analysis,
    summarization, embeddings, reranking, and content moderation.

    > **⚠ Platform preview.** This API is the developer-platform preview for AMBIE.
    > The endpoint shapes, auth, retry, webhook signatures, SDKs, and billing are stable.
    > Behind the scenes, every endpoint currently runs on commodity off-the-shelf models
    > (Deepgram Nova-3, OpenAI Whisper, Llama 3.1 70B, BGE family, BART, DistilBERT,
    > Llama Guard 3) served via Cloudflare Workers AI and Deepgram passthrough.
    >
    > **The proprietary AMBIE acoustic-intelligence models — what gives the platform its
    > 90-95% noisy-environment accuracy — are in active development and will replace the
    > transcription and TTS engines in 2027.** When that happens, the same API surface
    > stays; you'll get the upgrade with no code change. Translation, embeddings, rerank,
    > sentiment, summarize, moderate, and detect-language remain as utility wrappers
    > around well-understood OSS models — those won't get an "AMBIE" proprietary swap.
    >
    > See https://ambie.ai/preview/ for the full disclosure and 2027 roadmap. Email
    > cisco@ambie.ai if you're an early-access candidate (high volume, noisy-environment
    > use case, willingness to feedback during beta).
    >
    > Every API response carries `X-AMBIE-Preview: true` while the platform is in this
    > state, so SDKs and integrations can detect the preview-vs-GA transition programmatically.

    **Transcribe:** Two ASR engines (Deepgram Nova-3, OpenAI Whisper), four output formats,
    LLM-powered audio intelligence (summarization, key phrases, action items, chapters),
    speaker diarization, audio-to-English translation, and async webhook delivery.

    **Translate:** LLM-powered text translation with auto language detection, formality control,
    and domain-specific context hints. Powered by Llama 3.1 70B.

    **TTS:** Text-to-speech with Aura 2 (English/Spanish) and MeloTTS (6 languages).
    Multiple voices, encodings, and audio containers.

    **Sentiment:** Binary sentiment analysis (POSITIVE/NEGATIVE) with batch support.

    **Summarize:** Extractive text summarization with URL support. Free during beta.

    **Embeddings:** Text embeddings for semantic search with 5 model options (384-1024 dims).

    **Rerank:** Rerank search results by relevance using BGE Reranker. Pairs with embeddings.

    **Moderate:** Content safety screening with Llama Guard 3 (14 hazard categories).

    **Detect Language:** Standalone language detection with ISO 639-1 codes and confidence scores.
  version: 2.2.0
  contact:
    name: AMBIE
    url: https://ambie.ai
    email: contact@ambie.ai
  license:
    name: Proprietary

servers:
  - url: https://ambie.ai/api/v1
    description: Production

security:
  - BearerAuth: []

tags:
  - name: Transcription
    description: Audio transcription and intelligence
  - name: Translation
    description: Text translation between languages
  - name: TTS
    description: Text-to-speech synthesis
  - name: Sentiment
    description: Sentiment analysis
  - name: Summarization
    description: Text summarization
  - name: Embeddings
    description: Text embeddings for semantic search
  - name: Reranking
    description: Search result reranking
  - name: Moderation
    description: Content safety screening
  - name: Language Detection
    description: Identify text language
  - name: Jobs
    description: Async job status polling
  - name: Health
    description: Service health and capabilities

paths:
  /transcribe:
    get:
      tags: [Health]
      summary: Health check and API capabilities
      description: Returns service status, available engines, features, formats, and limits. No authentication required.
      security: []
      operationId: getHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"

    post:
      tags: [Transcription]
      summary: Transcribe audio
      description: |
        Transcribe an audio file with optional intelligence features. Supports synchronous
        (wait for result) and asynchronous (webhook callback) modes.

        **Sync mode:** Returns full result in response body.

        **Async mode:** When `callback_url` is provided, returns `202 Accepted` immediately
        with a `request_id` and `poll_url`. The full result is POSTed to `callback_url` when
        processing completes. Poll status at `/transcribe/{request_id}`.
      operationId: transcribeAudio
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/TranscribeRequest"
      responses:
        "200":
          description: Transcription complete (sync mode, format=json)
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TranscriptionResult"
            text/plain:
              schema:
                type: string
                description: Plain text transcript (format=text)
            application/x-subrip:
              schema:
                type: string
                description: SRT subtitle file (format=srt)
            text/vtt:
              schema:
                type: string
                description: WebVTT subtitle file (format=vtt)
        "202":
          description: Job accepted for async processing
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncAcceptedResponse"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                missingAudio:
                  summary: No audio file
                  value:
                    error: "Missing 'audio' file field"
                    request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                invalidEngine:
                  summary: Bad engine value
                  value:
                    error: 'Invalid engine: use "deepgram" or "whisper"'
                    request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                invalidFormat:
                  summary: Bad format value
                  value:
                    error: 'Invalid format: use "json", "text", "srt", or "vtt"'
                    request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                emptyFile:
                  summary: Zero-byte file
                  value:
                    error: "Audio file is empty"
                    request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                invalidCallback:
                  summary: Malformed callback URL
                  value:
                    error: "Invalid callback_url"
                    request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        "401":
          description: Missing authentication
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "413":
          description: Audio file too large (max 100 MB)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (30 req/min)
          headers:
            Retry-After:
              $ref: "#/components/headers/Retry-After"
            X-RateLimit-Remaining:
              schema:
                type: string
                example: "0"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "502":
          description: Transcription failed or timed out
          headers:
            Retry-After:
              $ref: "#/components/headers/Retry-After"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: Service unavailable (API key not configured or AI binding missing)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /transcribe/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async job status
      description: |
        Check the status of an async transcription job. Returns `202` while processing,
        `200` when completed or failed, `404` if the job doesn't exist.
      operationId: getJobStatus
      parameters:
        - name: request_id
          in: path
          required: true
          description: The request_id returned from the async POST
          schema:
            type: string
            format: uuid
            example: cc3300a2-69f0-408e-bb61-13febf246dba
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
              example:
                request_id: cc3300a2-69f0-408e-bb61-13febf246dba
                status: processing
                created_at: "2026-03-28T07:00:00.000Z"
                updated_at: "2026-03-28T07:00:00.000Z"
        "400":
          description: Invalid request_id format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Missing authentication
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Job not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: Job tracking not available (D1 not bound)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /translate:
    get:
      tags: [Health]
      summary: Translation health check and capabilities
      description: Returns translation service status, model, features, and limits. No authentication required.
      security: []
      operationId: getTranslateHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TranslateHealthResponse"

    post:
      tags: [Translation]
      summary: Translate text
      description: |
        Translate text between languages with optional formality control and domain context.
        Supports synchronous (wait for result) and asynchronous (webhook callback) modes.

        **Auto-detection:** Source language is automatically detected when not specified.
        Detection runs in parallel with translation, adding no extra latency.

        **Formality:** Control register with `formal`, `informal`, or `auto`.

        **Context:** Provide a domain hint (e.g. "legal document", "casual chat") to guide
        terminology and tone.

        **Async mode:** When `callback_url` is provided, returns `202 Accepted` immediately.
      operationId: translateText
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TranslateRequest"
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/TranslateRequest"
      responses:
        "200":
          description: Translation complete (sync mode, format=json)
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TranslationResult"
            text/plain:
              schema:
                type: string
                description: Translated text only (format=text)
        "202":
          description: Job accepted for async processing
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TranslateAsyncResponse"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                missingText:
                  summary: No text provided
                  value:
                    error: "Missing required field: text"
                    request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                missingTarget:
                  summary: No target language
                  value:
                    error: "Missing required field: target_lang"
                    request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                invalidFormality:
                  summary: Bad formality value
                  value:
                    error: 'Invalid formality: use "auto", "formal", or "informal"'
                    request_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        "401":
          description: Missing authentication
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "413":
          description: Text too long (max 50,000 chars)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (30 req/min)
          headers:
            Retry-After:
              $ref: "#/components/headers/Retry-After"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "502":
          description: Translation failed or timed out
          headers:
            Retry-After:
              $ref: "#/components/headers/Retry-After"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: Service unavailable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /translate/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async translation job status
      description: |
        Check the status of an async translation job. Returns `202` while processing,
        `200` when completed or failed, `404` if the job doesn't exist.
      operationId: getTranslateJobStatus
      parameters:
        - name: request_id
          in: path
          required: true
          description: The request_id returned from the async POST
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TranslateJobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TranslateJobStatusResponse"
        "400":
          description: Invalid request_id format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Missing authentication
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Job not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: Job tracking not available (D1 not bound)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  # --- TTS ---

  /tts:
    get:
      tags: [Health]
      summary: TTS health check and capabilities
      security: []
      operationId: getTtsHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TtsHealthResponse"

    post:
      tags: [TTS]
      summary: Convert text to speech
      description: |
        Synthesize speech from text using Aura 2 (English/Spanish) or MeloTTS (6 languages).

        **Models:** `aura-2-en` (default, 40+ voices), `aura-2-es` (10+ voices), `melotts` (6 langs).

        **Output:** Raw audio bytes (default) or JSON with base64-encoded audio.

        **Async mode:** When `callback_url` is provided, returns `202 Accepted` immediately.
      operationId: synthesizeSpeech
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TtsRequest"
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/TtsRequest"
      responses:
        "200":
          description: Audio synthesized successfully
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            audio/mpeg:
              schema:
                type: string
                format: binary
                description: Raw audio bytes (format=audio)
            application/json:
              schema:
                $ref: "#/components/schemas/TtsResult"
        "202":
          description: Job accepted for async processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncAcceptedResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          description: Text too long (max 10,000 chars)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "502":
          $ref: "#/components/responses/UpstreamError"
        "503":
          $ref: "#/components/responses/Unavailable"

  /tts/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async TTS job status
      operationId: getTtsJobStatus
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/Unavailable"

  # --- Sentiment ---

  /sentiment:
    get:
      tags: [Health]
      summary: Sentiment health check and capabilities
      security: []
      operationId: getSentimentHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SentimentHealthResponse"

    post:
      tags: [Sentiment]
      summary: Analyze text sentiment
      description: |
        Analyze sentiment of text using DistilBERT. Returns POSITIVE or NEGATIVE with confidence score.

        **Batch:** Send a JSON array of strings to analyze multiple texts in one request (max 100).
      operationId: analyzeSentiment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SentimentRequest"
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/SentimentRequest"
      responses:
        "200":
          description: Sentiment analysis complete
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SentimentResult"
            text/plain:
              schema:
                type: string
                description: One result per line (format=text)
        "202":
          description: Job accepted for async processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncAcceptedResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          description: Text too long (max 50,000 chars total)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "502":
          $ref: "#/components/responses/UpstreamError"
        "503":
          $ref: "#/components/responses/Unavailable"

  /sentiment/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async sentiment job status
      operationId: getSentimentJobStatus
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/Unavailable"

  # --- Summarize ---

  /summarize:
    get:
      tags: [Health]
      summary: Summarization health check and capabilities
      security: []
      operationId: getSummarizeHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SummarizeHealthResponse"

    post:
      tags: [Summarization]
      summary: Summarize text
      description: |
        Summarize text or web page content using BART Large CNN.
        Free during beta — no per-request cost.

        **URL input:** Provide a `url` to fetch and summarize a web page directly.
      operationId: summarizeText
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SummarizeRequest"
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/SummarizeRequest"
      responses:
        "200":
          description: Summarization complete
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SummarizeResult"
            text/plain:
              schema:
                type: string
                description: Summary text only (format=text)
        "202":
          description: Job accepted for async processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncAcceptedResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          description: Text too long (max 50,000 chars)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "502":
          $ref: "#/components/responses/UpstreamError"
        "503":
          $ref: "#/components/responses/Unavailable"

  /summarize/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async summarization job status
      operationId: getSummarizeJobStatus
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/Unavailable"

  # --- Embeddings ---

  /embeddings:
    get:
      tags: [Health]
      summary: Embeddings health check and capabilities
      security: []
      operationId: getEmbeddingsHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmbeddingsHealthResponse"

    post:
      tags: [Embeddings]
      summary: Generate text embeddings
      description: |
        Generate vector embeddings for semantic search, clustering, and similarity.

        **Models:** `bge-base-en-v1.5` (default, 768d), `bge-large-en-v1.5` (1024d),
        `bge-small-en-v1.5` (384d), `bge-m3` (multilingual, 60K tokens), `embeddinggemma` (100+ langs).

        **Batch:** Send a JSON array of strings to embed multiple texts (max 100).
      operationId: generateEmbeddings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EmbeddingsRequest"
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/EmbeddingsRequest"
      responses:
        "200":
          description: Embeddings generated successfully
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmbeddingsResult"
            text/plain:
              schema:
                type: string
                description: Tab-separated vectors, one per line (format=text)
        "202":
          description: Job accepted for async processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncAcceptedResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          description: Text too long (max 100,000 chars total)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "502":
          $ref: "#/components/responses/UpstreamError"
        "503":
          $ref: "#/components/responses/Unavailable"

  /embeddings/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async embeddings job status
      operationId: getEmbeddingsJobStatus
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/Unavailable"

  # --- Rerank ---

  /rerank:
    get:
      tags: [Health]
      summary: Rerank health check and capabilities
      security: []
      operationId: getRerankHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RerankHealthResponse"

    post:
      tags: [Reranking]
      summary: Rerank search results
      description: |
        Rerank a set of text contexts by relevance to a query using BGE Reranker.
        Returns sigmoid-scored results (0-1) sorted by relevance.

        **Use case:** After retrieving candidates via embeddings or keyword search,
        rerank them for precision before presenting to users.
      operationId: rerankTexts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RerankRequest"
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/RerankRequest"
      responses:
        "200":
          description: Reranking complete
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RerankResult"
            text/plain:
              schema:
                type: string
                description: "Tab-separated score and text per line (format=text)"
        "202":
          description: Job accepted for async processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncAcceptedResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          description: Query or contexts too long
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "502":
          $ref: "#/components/responses/UpstreamError"
        "503":
          $ref: "#/components/responses/Unavailable"

  /rerank/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async rerank job status
      operationId: getRerankJobStatus
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/Unavailable"

  # --- Moderate ---

  /moderate:
    get:
      tags: [Health]
      summary: Moderation health check and capabilities
      security: []
      operationId: getModerateHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModerateHealthResponse"

    post:
      tags: [Moderation]
      summary: Screen text for safety
      description: |
        Screen text for safety using Llama Guard 3. Returns a safe/unsafe verdict
        with flagged hazard categories (S1-S14).

        **Categories:** Violent Crimes, Non-Violent Crimes, Sex-Related Crimes,
        Child Sexual Exploitation, Defamation, Specialized Advice, Privacy,
        Intellectual Property, Indiscriminate Weapons, Hate, Suicide & Self-Harm,
        Sexual Content, Elections, Code Interpreter Abuse.
      operationId: moderateText
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ModerateRequest"
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/ModerateRequest"
      responses:
        "200":
          description: Moderation complete
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModerateResult"
            text/plain:
              schema:
                type: string
                description: "SAFE or UNSAFE with categories (format=text)"
        "202":
          description: Job accepted for async processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncAcceptedResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          description: Text too long (max 50,000 chars)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "502":
          $ref: "#/components/responses/UpstreamError"
        "503":
          $ref: "#/components/responses/Unavailable"

  /moderate/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async moderation job status
      operationId: getModerateJobStatus
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/Unavailable"

  # --- Detect Language ---

  /detect-lang:
    get:
      tags: [Health]
      summary: Language detection health check and capabilities
      security: []
      operationId: getDetectLangHealth
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DetectLangHealthResponse"

    post:
      tags: [Language Detection]
      summary: Detect text language
      description: |
        Detect the language of text, returning ISO 639-1 codes with confidence scores.

        **Batch:** Send a JSON array of strings to detect multiple texts (max 50).
      operationId: detectLanguage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DetectLangRequest"
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/DetectLangRequest"
      responses:
        "200":
          description: Detection complete
          headers:
            X-Request-Id:
              $ref: "#/components/headers/X-Request-Id"
            X-RateLimit-Remaining:
              $ref: "#/components/headers/X-RateLimit-Remaining"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DetectLangResult"
            text/plain:
              schema:
                type: string
                description: "Tab-separated code, name, confidence, text per line"
        "202":
          description: Job accepted for async processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AsyncAcceptedResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          description: Text too long (max 50,000 chars total)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalError"
        "502":
          $ref: "#/components/responses/UpstreamError"
        "503":
          $ref: "#/components/responses/Unavailable"

  /detect-lang/{request_id}:
    get:
      tags: [Jobs]
      summary: Poll async language detection job status
      operationId: getDetectLangJobStatus
      parameters:
        - $ref: "#/components/parameters/RequestId"
      responses:
        "200":
          description: Job completed or failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "202":
          description: Job still processing
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JobStatusResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "503":
          $ref: "#/components/responses/Unavailable"

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: API key provided as Bearer token

  headers:
    X-Request-Id:
      description: Unique request identifier for log correlation
      schema:
        type: string
        format: uuid
    X-RateLimit-Remaining:
      description: Requests remaining in current rate limit window
      schema:
        type: string
    Retry-After:
      description: Seconds to wait before retrying
      schema:
        type: string

  schemas:
    TranscribeRequest:
      type: object
      description: "Provide either 'audio' (file upload) or 'url' (remote fetch)."
      properties:
        audio:
          type: string
          format: binary
          description: "Audio file (mp3, mp4, mp2, aac, wav, flac, pcm, m4a, ogg, opus, webm). Max 100 MB. Provide either audio or url."
        url:
          type: string
          format: uri
          description: "URL to audio file (alternative to upload). CF fetches server-side. Max 100 MB. Large files auto-chunked."
          example: https://cdn.example.com/recordings/meeting.mp3
        engine:
          type: string
          enum: [deepgram, whisper]
          default: deepgram
          description: "ASR engine. Deepgram for accuracy/diarization, Whisper for multilingual/translation."
        language:
          type: string
          description: "BCP-47 language code hint (e.g. en, es, fr-CA). Auto-detected if omitted."
          example: en
        format:
          type: string
          enum: [json, text, srt, vtt]
          default: json
          description: "Output format. Non-json formats return raw content with appropriate Content-Type."
        translate:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Translate audio to English (Whisper engine only)."
        callback_url:
          type: string
          format: uri
          description: "Webhook URL. When provided, API returns 202 immediately and POSTs result to this URL."
          example: https://yourserver.com/webhooks/transcription
        client_id:
          type: string
          description: "Your correlation ID. Echoed in all responses, webhook deliveries, and poll results."
          example: meeting-2026-03-28
        punctuate:
          type: string
          enum: ["true", "false"]
          default: "true"
          description: Add punctuation and capitalization.
        smart_format:
          type: string
          enum: ["true", "false"]
          default: "true"
          description: Apply formatting for improved readability.
        diarize:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Detect and label speakers (Deepgram only). Adds diarized_text and speaker_count."
        detect_entities:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Extract named entities (Deepgram only)."
        paragraphs:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Split transcript into paragraphs (Deepgram only)."
        utterances:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Segment into semantic utterances (Deepgram only)."
        filler_words:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: 'Include filler words like "uh" and "um" (Deepgram only).'
        profanity_filter:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Replace profanity with asterisks (Deepgram only)."
        numerals:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Convert spoken numbers to digits (Deepgram only)."
        measurements:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Abbreviate spoken measurements (Deepgram only)."
        multichannel:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Transcribe each audio channel independently (Deepgram only)."
        vocabulary:
          type: string
          description: "Comma-separated terms to boost recognition (Whisper only). Helps with names, acronyms, and domain-specific jargon."
          example: "AMBIE, Abundera, Trivlet, ASR, diarization"
        prefix:
          type: string
          description: "Guide the start of the transcription output (Whisper only)."
          example: "Meeting notes:"
        summarize:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Generate LLM summary of the transcript."
        key_phrases:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Extract key phrases, terms, names, and topics via LLM."
        action_items:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Extract action items, tasks, and commitments via LLM."
        chapters:
          type: string
          enum: ["true", "false"]
          default: "false"
          description: "Split transcript into timestamped topic chapters via LLM."

    Word:
      type: object
      required: [word, start, end]
      properties:
        word:
          type: string
          example: Americans,
        start:
          type: number
          format: float
          description: Start time in seconds
          example: 2.4
        end:
          type: number
          format: float
          description: End time in seconds
          example: 3.36
        confidence:
          type: number
          format: float
          description: "Confidence score (0-1)"
          example: 0.984
        speaker:
          type: integer
          description: "Speaker number (present when diarize=true)"
          example: 0
        channel:
          type: integer
          description: "Audio channel (present when multichannel=true)"

    Utterance:
      type: object
      properties:
        text:
          type: string
        start:
          type: number
          format: float
        end:
          type: number
          format: float
        confidence:
          type: number
          format: float
        speaker:
          type: integer

    ActionItem:
      type: object
      properties:
        action:
          type: string
          description: Description of the action item
          example: Follow up with vendor on pricing
        assignee:
          type: string
          nullable: true
          description: "Who is responsible (Speaker N or null)"
          example: Speaker 1
        deadline:
          type: string
          nullable: true
          description: "When it's due (if mentioned)"
          example: Friday

    Chapter:
      type: object
      properties:
        title:
          type: string
          description: "Concise chapter title (3-8 words)"
          example: Budget Discussion
        summary:
          type: string
          description: "1-2 sentence summary"
          example: Team reviews Q2 numbers and discusses budget allocation.
        start_phrase:
          type: string
          description: "First few words of this section in the transcript"
          example: Let's look at the numbers

    FileInfo:
      type: object
      properties:
        name:
          type: string
          nullable: true
          example: meeting.mp3
        size_bytes:
          type: integer
          example: 352078
        type:
          type: string
          nullable: true
          example: audio/mpeg
        source:
          type: string
          enum: [upload, url]
          description: "How the audio was provided"

    TranscriptionResult:
      type: object
      required: [text, language, word_count, words, model, engine, processing_ms, request_id, processed_at, file]
      properties:
        text:
          type: string
          description: Full transcript text
        language:
          type: string
          description: Detected or specified language
          example: en
        language_probability:
          type: number
          format: float
          nullable: true
          description: "Language detection confidence (Whisper only)"
        duration_seconds:
          type: number
          format: float
          nullable: true
          description: Audio duration in seconds (parsed from file headers)
          example: 63.66
        transcript_confidence:
          type: number
          format: float
          nullable: true
          description: "Average word confidence (0-1)"
          example: 0.983
        word_count:
          type: integer
          example: 152
        words:
          type: array
          items:
            $ref: "#/components/schemas/Word"
        srt:
          type: string
          description: SRT subtitle content generated from word timestamps
        vtt:
          type: string
          description: WebVTT subtitle content generated from word timestamps
        model:
          type: string
          example: "@cf/deepgram/nova-3"
        engine:
          type: string
          enum: [deepgram, whisper]
        processing_ms:
          type: integer
          description: ASR processing time in milliseconds
          example: 2405
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        file:
          $ref: "#/components/schemas/FileInfo"
        client_id:
          type: string
          description: "Echoed from request (if provided)"
        translated:
          type: boolean
          description: "True if translation was applied (Whisper only)"
        diarized_text:
          type: string
          nullable: true
          description: "Transcript with [Speaker N] labels (when diarize=true)"
        speaker_count:
          type: integer
          nullable: true
          description: "Number of speakers detected (when diarize=true)"
        utterances:
          type: array
          items:
            $ref: "#/components/schemas/Utterance"
          description: "Semantic utterances (when utterances=true)"
        paragraphs:
          type: object
          nullable: true
          description: "Paragraph structure (when paragraphs=true)"
        entities:
          type: object
          nullable: true
          description: "Named entities (when detect_entities=true)"
        channels:
          type: array
          description: "Per-channel transcripts (when multichannel=true, stereo+ audio)"
          items:
            type: object
            properties:
              channel:
                type: integer
              transcript:
                type: string
              confidence:
                type: number
                format: float
                nullable: true
        segments:
          type: array
          description: "Whisper segments with logprobs (Whisper engine only)"
          items:
            type: object
            properties:
              text:
                type: string
              start:
                type: number
                format: float
              end:
                type: number
                format: float
              avg_logprob:
                type: number
                format: float
              no_speech_prob:
                type: number
                format: float
              words:
                type: array
                items:
                  $ref: "#/components/schemas/Word"
        summary:
          type: string
          nullable: true
          description: "LLM-generated summary (when summarize=true)"
        summary_model:
          type: string
          description: "Model used for summarization"
        summary_error:
          type: string
          description: "Error message if summarization failed"
        key_phrases:
          type: array
          nullable: true
          items:
            type: string
          description: "Extracted key phrases (when key_phrases=true)"
          example: ["fellow Americans", "civic duty", "country"]
        key_phrases_error:
          type: string
        action_items:
          type: array
          nullable: true
          items:
            $ref: "#/components/schemas/ActionItem"
          description: "Extracted action items (when action_items=true)"
        action_items_error:
          type: string
        chapters:
          type: array
          nullable: true
          items:
            $ref: "#/components/schemas/Chapter"
          description: "Topic chapters (when chapters=true)"
        chapters_error:
          type: string
        intelligence_ms:
          type: integer
          description: "Total LLM processing time for all intelligence features"
          example: 1200
        chunks_processed:
          type: integer
          description: "Number of chunks (present when large file was auto-chunked)"
          example: 11

    AsyncAcceptedResponse:
      type: object
      required: [request_id, status, poll_url, callback_url]
      properties:
        request_id:
          type: string
          format: uuid
          example: cc3300a2-69f0-408e-bb61-13febf246dba
        status:
          type: string
          enum: [processing]
        poll_url:
          type: string
          format: uri
          example: https://ambie.ai/api/v1/transcribe/cc3300a2-69f0-408e-bb61-13febf246dba
        callback_url:
          type: string
          format: uri
        client_id:
          type: string
          description: "Echoed from request (if provided)"

    JobStatusResponse:
      type: object
      required: [request_id, status, created_at, updated_at]
      properties:
        request_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [processing, completed, failed]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        result:
          $ref: "#/components/schemas/TranscriptionResult"
          description: "Full result (when status=completed)"
        error:
          type: string
          description: "Error message (when status=failed)"

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Transcribe API
        version:
          type: string
          example: 1.0.0
        engines:
          type: object
          properties:
            deepgram:
              type: object
              properties:
                model:
                  type: string
                features:
                  type: array
                  items:
                    type: string
            whisper:
              type: object
              properties:
                model:
                  type: string
                features:
                  type: array
                  items:
                    type: string
        intelligence:
          type: array
          items:
            type: string
          example: [summarize, key_phrases, action_items, chapters]
        formats:
          type: array
          items:
            type: string
          example: [json, text, srt, vtt]
        supported_audio:
          type: string
          example: "mp3, mp4, mp2, aac, wav, flac, pcm, m4a, ogg, opus, webm"
        max_file_size_mb:
          type: integer
          example: 100
        rate_limit:
          type: string
          example: "30 requests per 60s"
        ai_available:
          type: boolean

    TranslateRequest:
      type: object
      required: [target_lang]
      description: "Provide either 'text' (direct input) or 'url' (fetch and extract from web page)."
      properties:
        text:
          type: string
          description: "Text to translate (max 50,000 characters). Provide either text or url."
          example: "Hello, how are you? I hope you are having a wonderful day."
        url:
          type: string
          format: uri
          description: "URL to fetch and translate. HTML text is extracted automatically. Max 5 MB page size, 50,000 chars after extraction."
          example: https://example.com/article
        target_lang:
          type: string
          description: "Target language — name or BCP-47 code (e.g. 'Spanish', 'ja', 'fr-CA', 'zh-CN')"
          example: Spanish
        source_lang:
          type: string
          description: "Source language (auto-detected if omitted)"
          example: en
        engine:
          type: string
          enum: [llm, m2m100]
          default: llm
          description: "Translation engine. 'llm' = Llama 3.1 70B (formality, context, auto-detect). 'm2m100' = dedicated NMT (~10x faster, no formality/context)."
        formality:
          type: string
          enum: [auto, formal, informal]
          default: auto
          description: "Register control. 'formal' uses polite/professional forms, 'informal' uses casual tone."
        context:
          type: string
          description: "Domain hint to guide terminology and tone"
          example: "business email"
        format:
          type: string
          enum: [json, text]
          default: json
          description: "Output format. 'text' returns only the translated string."
        callback_url:
          type: string
          format: uri
          description: "Webhook URL. When provided, API returns 202 and POSTs result when done."
          example: https://yourserver.com/webhooks/translation
        client_id:
          type: string
          description: "Your correlation ID. Echoed in all responses and webhooks."
          example: doc-2026-04-02

    TranslationResult:
      type: object
      required: [translated_text, source_lang, target_lang, formality, input_chars, output_chars, model, processing_ms, request_id, processed_at]
      properties:
        translated_text:
          type: string
          description: The translated text
          example: "Hola, ¿cómo estás? Espero que estés teniendo un día maravilloso."
        source_lang:
          type: string
          description: "Detected or specified source language code"
          example: en
        source_lang_name:
          type: string
          nullable: true
          description: "Full name of detected source language"
          example: English
        source_lang_confidence:
          type: number
          format: float
          nullable: true
          description: "Language detection confidence (0-1)"
          example: 0.99
        target_lang:
          type: string
          example: Spanish
        formality:
          type: string
          enum: [auto, formal, informal]
        input_chars:
          type: integer
          description: Character count of input text
          example: 58
        output_chars:
          type: integer
          description: Character count of translated text
          example: 64
        model:
          type: string
          example: "@cf/meta/llama-3.1-70b-instruct"
        processing_ms:
          type: integer
          description: Processing time in milliseconds
          example: 1374
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        client_id:
          type: string
          description: "Echoed from request (if provided)"
        source:
          type: object
          nullable: true
          description: "Present when url parameter was used"
          properties:
            url:
              type: string
              format: uri
            content_type:
              type: string
              nullable: true
            extracted_chars:
              type: integer
              description: "Characters extracted from the page"

    TranslateAsyncResponse:
      type: object
      required: [request_id, status, poll_url, callback_url]
      properties:
        request_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [processing]
        poll_url:
          type: string
          format: uri
          example: https://ambie.ai/api/v1/translate/cc3300a2-69f0-408e-bb61-13febf246dba
        callback_url:
          type: string
          format: uri
        client_id:
          type: string

    TranslateJobStatusResponse:
      type: object
      required: [request_id, status, created_at, updated_at]
      properties:
        request_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [processing, completed, failed]
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        result:
          $ref: "#/components/schemas/TranslationResult"
          description: "Full result (when status=completed)"
        error:
          type: string
          description: "Error message (when status=failed)"

    TranslateHealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Translate API
        version:
          type: string
          example: 1.0.0
        model:
          type: string
          example: "@cf/meta/llama-3.1-70b-instruct"
        features:
          type: object
          properties:
            auto_detect:
              type: string
            formality:
              type: array
              items:
                type: string
            context:
              type: string
            async:
              type: object
        formats:
          type: array
          items:
            type: string
          example: [json, text]
        max_text_length:
          type: integer
          example: 50000
        rate_limit:
          type: string
          example: "30 requests per 60s"
        ai_available:
          type: boolean

    # --- TTS Schemas ---

    TtsRequest:
      type: object
      required: [text]
      properties:
        text:
          type: string
          description: Text to synthesize (max 10,000 chars)
          maxLength: 10000
          example: "Hello, welcome to Ambie."
        model:
          type: string
          enum: [aura-2-en, aura-2-es, melotts]
          default: aura-2-en
          description: TTS model to use
        voice:
          type: string
          description: "Voice name (e.g. luna, apollo, aquila). Model-specific."
          example: luna
        lang:
          type: string
          enum: [en, es, fr, zh, ja, ko]
          default: en
          description: Language for MeloTTS model
        encoding:
          type: string
          enum: [mp3, linear16, flac, mulaw, alaw, opus, aac]
          default: mp3
        container:
          type: string
          enum: [none, wav, ogg]
          default: none
        sample_rate:
          type: integer
          description: Sample rate in Hz
          example: 24000
        format:
          type: string
          enum: [audio, json]
          default: audio
          description: "audio = raw bytes, json = base64-encoded"
        callback_url:
          type: string
          format: uri
        client_id:
          type: string

    TtsResult:
      type: object
      properties:
        audio_base64:
          type: string
          description: Base64-encoded audio data (format=json only)
        model:
          type: string
          example: "@cf/deepgram/aura-2-en"
        model_name:
          type: string
          example: aura-2-en
        voice:
          type: string
          example: luna
        lang:
          type: string
        input_chars:
          type: integer
        encoding:
          type: string
        processing_ms:
          type: integer
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        client_id:
          type: string

    TtsHealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Text-to-Speech API
        version:
          type: string
        models:
          type: object
        features:
          type: object
        max_text_length:
          type: integer
          example: 10000
        rate_limit:
          type: string
        ai_available:
          type: boolean

    # --- Sentiment Schemas ---

    SentimentRequest:
      type: object
      required: [text]
      properties:
        text:
          oneOf:
            - type: string
              description: Single text to analyze
            - type: array
              items:
                type: string
              description: Batch of texts (max 100)
          example: "This product is absolutely amazing!"
        format:
          type: string
          enum: [json, text]
          default: json
        callback_url:
          type: string
          format: uri
        client_id:
          type: string

    SentimentResult:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              text:
                type: string
              label:
                type: string
                enum: [POSITIVE, NEGATIVE]
              score:
                type: number
                example: 0.9997
              scores:
                type: array
                items:
                  type: object
                  properties:
                    label:
                      type: string
                    score:
                      type: number
        model:
          type: string
          example: "@cf/huggingface/distilbert-sst-2-int8"
        total_items:
          type: integer
        processing_ms:
          type: integer
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        client_id:
          type: string

    SentimentHealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Sentiment Analysis API
        model:
          type: string
        features:
          type: object
        max_batch_size:
          type: integer
          example: 100
        rate_limit:
          type: string
        ai_available:
          type: boolean

    # --- Summarize Schemas ---

    SummarizeRequest:
      type: object
      properties:
        text:
          type: string
          description: "Text to summarize (max 50,000 chars). Provide text OR url."
          maxLength: 50000
        url:
          type: string
          format: uri
          description: URL to fetch and summarize
          example: https://en.wikipedia.org/wiki/Artificial_intelligence
        max_length:
          type: integer
          default: 1024
          maximum: 1024
          description: Maximum summary length in tokens
        format:
          type: string
          enum: [json, text]
          default: json
        callback_url:
          type: string
          format: uri
        client_id:
          type: string

    SummarizeResult:
      type: object
      properties:
        summary:
          type: string
          description: The generated summary
        model:
          type: string
          example: "@cf/facebook/bart-large-cnn"
        input_chars:
          type: integer
        output_chars:
          type: integer
        max_length:
          type: integer
        processing_ms:
          type: integer
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        source:
          type: object
          description: Present when url was used
          properties:
            url:
              type: string
            content_type:
              type: string
            extracted_chars:
              type: integer
        client_id:
          type: string

    SummarizeHealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Summarization API
        model:
          type: string
        features:
          type: object
        max_text_length:
          type: integer
          example: 50000
        max_summary_length:
          type: integer
          example: 1024
        rate_limit:
          type: string
        ai_available:
          type: boolean

    # --- Embeddings Schemas ---

    EmbeddingsRequest:
      type: object
      required: [text]
      properties:
        text:
          oneOf:
            - type: string
              description: Single text to embed
            - type: array
              items:
                type: string
              description: Batch of texts (max 100)
          example: "Semantic search query"
        model:
          type: string
          enum: [bge-base-en-v1.5, bge-large-en-v1.5, bge-small-en-v1.5, bge-m3, embeddinggemma]
          default: bge-base-en-v1.5
        format:
          type: string
          enum: [json, text]
          default: json
          description: "json = full response, text = tab-separated vectors"
        callback_url:
          type: string
          format: uri
        client_id:
          type: string

    EmbeddingsResult:
      type: object
      properties:
        data:
          type: array
          items:
            type: array
            items:
              type: number
          description: Array of embedding vectors
        shape:
          type: array
          items:
            type: integer
          example: [1, 768]
        model:
          type: string
          example: "@cf/baai/bge-base-en-v1.5"
        model_name:
          type: string
          example: bge-base-en-v1.5
        dimensions:
          type: integer
          example: 768
        total_items:
          type: integer
        total_chars:
          type: integer
        processing_ms:
          type: integer
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        client_id:
          type: string

    EmbeddingsHealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Embeddings API
        default_model:
          type: string
        models:
          type: object
        max_batch_size:
          type: integer
          example: 100
        rate_limit:
          type: string
        ai_available:
          type: boolean

    # --- Rerank Schemas ---

    RerankRequest:
      type: object
      required: [query, contexts]
      properties:
        query:
          type: string
          description: The search query to rank against (max 1,000 chars)
          maxLength: 1000
          example: "best restaurant in Las Vegas"
        contexts:
          type: array
          items:
            type: string
          description: Texts to rerank by relevance (max 100)
          example:
            - "Fine dining at Bellagio with a prix fixe menu"
            - "Pizza place on the strip open late"
            - "Weather forecast for Tuesday"
        top_k:
          type: integer
          description: Return only the top K most relevant results
          example: 5
        format:
          type: string
          enum: [json, text]
          default: json
        callback_url:
          type: string
          format: uri
        client_id:
          type: string

    RerankResult:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              index:
                type: integer
                description: Original index in the contexts array
              score:
                type: number
                description: Relevance score (0-1, sigmoid)
                example: 0.8923
              text:
                type: string
                description: The original context text
        query:
          type: string
        model:
          type: string
          example: "@cf/baai/bge-reranker-base"
        total_contexts:
          type: integer
        returned:
          type: integer
        top_k:
          type: integer
        processing_ms:
          type: integer
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        client_id:
          type: string

    RerankHealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Rerank API
        model:
          type: string
        features:
          type: object
        max_contexts:
          type: integer
          example: 100
        rate_limit:
          type: string
        ai_available:
          type: boolean

    # --- Moderate Schemas ---

    ModerateRequest:
      type: object
      required: [text]
      properties:
        text:
          type: string
          description: Text to screen for safety (max 50,000 chars)
          maxLength: 50000
          example: "How do I bake a chocolate cake?"
        format:
          type: string
          enum: [json, text]
          default: json
        callback_url:
          type: string
          format: uri
        client_id:
          type: string

    ModerateResult:
      type: object
      properties:
        safe:
          type: boolean
          description: Whether the text is safe
          example: true
        flagged:
          type: boolean
          description: Inverse of safe (true = unsafe)
          example: false
        categories:
          type: array
          items:
            type: object
            properties:
              code:
                type: string
                example: S10
              name:
                type: string
                example: Hate
          description: Flagged hazard categories (empty if safe)
        category_count:
          type: integer
          example: 0
        model:
          type: string
          example: "@cf/meta/llama-guard-3-8b"
        input_chars:
          type: integer
        processing_ms:
          type: integer
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        client_id:
          type: string

    ModerateHealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Content Moderation API
        model:
          type: string
        features:
          type: object
        categories:
          type: object
          description: Map of S1-S14 hazard category codes to names
        max_text_length:
          type: integer
          example: 50000
        rate_limit:
          type: string
        ai_available:
          type: boolean

    # --- Detect Language Schemas ---

    DetectLangRequest:
      type: object
      required: [text]
      properties:
        text:
          oneOf:
            - type: string
              description: Single text to analyze
            - type: array
              items:
                type: string
              description: Batch of texts (max 50)
          example: "Bonjour, comment allez-vous?"
        format:
          type: string
          enum: [json, text]
          default: json
        callback_url:
          type: string
          format: uri
        client_id:
          type: string

    DetectLangResult:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              text:
                type: string
                description: Input text (truncated to 100 chars)
              language:
                type: string
                description: ISO 639-1 language code
                example: fr
              language_name:
                type: string
                example: French
              confidence:
                type: number
                example: 0.98
        model:
          type: string
        total_items:
          type: integer
        processing_ms:
          type: integer
        request_id:
          type: string
          format: uuid
        processed_at:
          type: string
          format: date-time
        client_id:
          type: string

    DetectLangHealthResponse:
      type: object
      properties:
        status:
          type: string
          example: ok
        service:
          type: string
          example: AMBIE Language Detection API
        model:
          type: string
        features:
          type: object
        max_batch_size:
          type: integer
          example: 50
        rate_limit:
          type: string
        ai_available:
          type: boolean

    # --- Shared Schemas ---

    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: string
          description: Human-readable error message
        detail:
          type: string
          description: "Additional error context (on 502 errors)"
        request_id:
          type: string
          format: uuid

  parameters:
    RequestId:
      name: request_id
      in: path
      required: true
      description: The request_id returned from the async POST
      schema:
        type: string
        format: uuid

  responses:
    BadRequest:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unauthorized:
      description: Missing authentication
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Forbidden:
      description: Invalid API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          $ref: "#/components/headers/Retry-After"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    UpstreamError:
      description: Upstream AI model failed or timed out
      headers:
        Retry-After:
          $ref: "#/components/headers/Retry-After"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unavailable:
      description: Service unavailable
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
