{
  "openapi": "3.1.0",
  "info": { "title": "Bombyx Widget Dictation Recovery", "version": "2.0.0", "description": "Optional recovery protocol on the existing trusted-widget dictation endpoint. Existing site/domain, widget token, frame and signed-proxy authorization applies to all requests, including status.", "license": { "name": "Bombyx service terms", "url": "https://bombyxlabs.com/terms/" } },
  "security": [],
  "x-bombyx-authorization": "The existing contextual widget authorization uses multipart action tokens, origin/domain checks and optional signed proxy/frame proof, rather than an OpenAPI bearer-header scheme. Every request, including status, must still pass those checks and the per-recording private recovery proof.",
  "servers": [{ "url": "https://bombyxlabs.com" }],
  "paths": {
    "/api/widget/{key}/dictation": {
      "post": {
        "operationId": "recoverWidgetDictation",
        "summary": "Transcribe a recording or recover its result",
        "description": "Three server-counted provider attempts per recording. Status never invokes a provider. Retain the same UUID, private key and recording bytes across retries. Recovery text is encrypted and unavailable after 24 hours. Do not log keys or content.",
        "parameters": [{ "name": "key", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "required": true,
          "content": { "multipart/form-data": { "schema": {
            "type": "object",
            "required": ["dictation_protocol", "dictation_action", "request_id", "recovery_key"],
            "properties": {
              "dictation_protocol": { "const": "2" },
              "dictation_action": { "enum": ["transcribe", "status"] },
              "request_id": { "type": "string", "format": "uuid" },
              "recovery_key": { "type": "string", "pattern": "^[a-f0-9]{64}$", "writeOnly": true },
              "audio": { "type": "string", "format": "binary", "description": "Required for transcribe; prohibited for status. Existing media limits apply." },
              "duration_seconds": { "type": "number", "minimum": 1, "maximum": 60, "description": "Omit for status. Existing endpoint-specific limits apply." },
              "widget_token": { "type": "string", "writeOnly": true },
              "frame_auth_token": { "type": "string", "writeOnly": true },
              "visitor_id": { "type": "string", "description": "Existing context only; never recovery authorization." },
              "page_url": { "type": "string" },
              "client_attempt": { "type": "integer", "minimum": 1, "description": "Transport metadata; cannot change server paid-attempt accounting." }
            }
          } } }
        },
        "responses": {
          "200": { "description": "Completed or cached text", "content": { "application/json": { "schema": { "type": "object", "required": ["text", "state"], "properties": { "text": { "type": "string" }, "state": { "const": "completed" }, "ok": { "type": "boolean" } } } } } },
          "202": { "description": "An existing attempt is processing; wait before a status request", "content": { "application/json": { "schema": { "type": "object", "required": ["state", "retry_after"], "properties": { "state": { "const": "processing" }, "retry_after": { "type": "integer", "minimum": 1 } } } } } },
          "403": { "description": "Current widget access or private recovery proof is invalid" },
          "404": { "description": "Unknown recording. Retryable missing receipt permits uploading the original recording and key." },
          "409": { "description": "Recording bytes changed (permanent), or a previous attempt can be retried. Follow retryable metadata." },
          "410": { "description": "Expired recording; old identity cannot restart attempts" },
          "422": { "description": "Invalid audio, no speech or exhausted attempts" },
          "429": { "description": "Rate limited; honor retry_after" },
          "503": { "description": "Temporarily unavailable; follow retryable metadata" }
        }
      }
    }
  }
}
