{
  "openapi": "3.1.0",
  "info": {
    "title": "AI Subtitle Translator API",
    "version": "1.0.0",
    "description": "Basic / Plus subtitle translation using account credits. Create keys in Dashboard → Security. 60 requests and 10 POST requests per minute per account; 3 active API tasks. See /api-docs.html."
  },
  "servers": [
    {
      "url": "/",
      "description": "Current website origin"
    }
  ],
  "security": [
    {
      "ApiKey": []
    }
  ],
  "paths": {
    "/api/v1/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "responses": {
          "200": {
            "description": "Capabilities",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Capabilities"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "402": {
            "$ref": "#/components/responses/Error402"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "410": {
            "$ref": "#/components/responses/Error410"
          },
          "413": {
            "$ref": "#/components/responses/Error413"
          },
          "415": {
            "$ref": "#/components/responses/Error415"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "502": {
            "$ref": "#/components/responses/Error502"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/api/v1/credits": {
      "get": {
        "operationId": "getCredits",
        "responses": {
          "200": {
            "description": "Credits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Credits"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "402": {
            "$ref": "#/components/responses/Error402"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "410": {
            "$ref": "#/components/responses/Error410"
          },
          "413": {
            "$ref": "#/components/responses/Error413"
          },
          "415": {
            "$ref": "#/components/responses/Error415"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "502": {
            "$ref": "#/components/responses/Error502"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/api/v1/translations/estimate": {
      "post": {
        "operationId": "estimateTranslation",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranslationInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Estimate only; credits are not reserved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Estimate"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "402": {
            "$ref": "#/components/responses/Error402"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "410": {
            "$ref": "#/components/responses/Error410"
          },
          "413": {
            "$ref": "#/components/responses/Error413"
          },
          "415": {
            "$ref": "#/components/responses/Error415"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "502": {
            "$ref": "#/components/responses/Error502"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/api/v1/translations": {
      "post": {
        "operationId": "createTranslation",
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9._:-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranslationInput"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted and queued; credits reserved. Reuse the same Idempotency-Key for retries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Translation"
                    }
                  }
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "false"
                  ]
                }
              }
            }
          },
          "200": {
            "description": "Identical replay returns the original task without creating or charging for a second task.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Translation"
                    }
                  }
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "Idempotent-Replayed": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "402": {
            "$ref": "#/components/responses/Error402"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "410": {
            "$ref": "#/components/responses/Error410"
          },
          "413": {
            "$ref": "#/components/responses/Error413"
          },
          "415": {
            "$ref": "#/components/responses/Error415"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "502": {
            "$ref": "#/components/responses/Error502"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/api/v1/translations/{taskId}": {
      "get": {
        "operationId": "getTranslation",
        "description": "Poll every 5 seconds.",
        "parameters": [
          {
            "in": "path",
            "name": "taskId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task status and progress",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Progress"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "402": {
            "$ref": "#/components/responses/Error402"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "410": {
            "$ref": "#/components/responses/Error410"
          },
          "413": {
            "$ref": "#/components/responses/Error413"
          },
          "415": {
            "$ref": "#/components/responses/Error415"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "502": {
            "$ref": "#/components/responses/Error502"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    },
    "/api/v1/translations/{taskId}/result": {
      "get": {
        "operationId": "getTranslationResult",
        "parameters": [
          {
            "in": "path",
            "name": "taskId",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "in": "query",
            "name": "includeOriginal",
            "description": "Include source text before each translation for bilingual SRT. Omit or set false for translation only.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Completed subtitle file. Success always returns downloadable UTF-8 SRT; errors use JSON. The format parameter is not supported.",
            "content": {
              "application/x-subrip": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "402": {
            "$ref": "#/components/responses/Error402"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "410": {
            "$ref": "#/components/responses/Error410"
          },
          "413": {
            "$ref": "#/components/responses/Error413"
          },
          "415": {
            "$ref": "#/components/responses/Error415"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "502": {
            "$ref": "#/components/responses/Error502"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ast_<32 random bytes, base64url>",
        "description": "Customer API key; website sessions and internal worker tokens are not accepted."
      }
    },
    "schemas": {
      "TranslationInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "subtitle",
          "targetLanguage",
          "modelTier"
        ],
        "properties": {
          "fileName": {
            "type": "string",
            "default": "subtitles.srt",
            "minLength": 1,
            "maxLength": 200,
            "pattern": "^[^\\x00-\\x1f\\x7f/\\\\]+\\.[sS][rR][tT]$"
          },
          "subtitle": {
            "type": "string",
            "minLength": 1,
            "description": "UTF-8 SRT, at most 1 MiB and 5,000 cues. Numbered cues, HH:MM:SS,mmm timestamps, non-empty text."
          },
          "targetLanguage": {
            "type": "string",
            "description": "Language code from /api/v1/capabilities, for example zh or zh-hant."
          },
          "modelTier": {
            "type": "string",
            "enum": [
              "basic",
              "plus"
            ],
            "description": "Required for every estimate and submission. No default model is selected."
          },
          "glossary": {
            "type": "array",
            "maxItems": 200,
            "default": [],
            "items": {
              "type": "object",
              "required": [
                "source",
                "target"
              ],
              "additionalProperties": false,
              "properties": {
                "source": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200
                },
                "target": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200
                }
              }
            }
          },
          "characters": {
            "type": "array",
            "maxItems": 100,
            "default": [],
            "items": {
              "type": "object",
              "required": [
                "source",
                "target"
              ],
              "additionalProperties": false,
              "properties": {
                "source": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200
                },
                "target": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200
                },
                "gender": {
                  "type": "string",
                  "enum": [
                    "male",
                    "female"
                  ]
                },
                "aliases": {
                  "type": "array",
                  "maxItems": 20,
                  "items": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  }
                }
              }
            }
          }
        }
      },
      "Translation": {
        "type": "object",
        "required": [
          "id",
          "status",
          "fileName",
          "modelTier",
          "targetLanguage",
          "creditCost",
          "statusUrl",
          "resultUrl"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "completed",
              "failed"
            ]
          },
          "fileName": {
            "type": "string"
          },
          "modelTier": {
            "type": "string",
            "enum": [
              "basic",
              "plus"
            ]
          },
          "targetLanguage": {
            "type": "string"
          },
          "sourceLanguage": {
            "type": [
              "string",
              "null"
            ]
          },
          "creditCost": {
            "type": "string",
            "description": "Decimal credits reserved at creation and charged once on success. A hold remains valid for this task after the source credits expire. Failure/deletion releases unexpired credits; expired credits remain expired."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "statusUrl": {
            "type": "string"
          },
          "resultUrl": {
            "type": "string"
          }
        }
      },
      "Estimate": {
        "type": "object",
        "properties": {
          "modelTier": {
            "type": "string"
          },
          "targetLanguage": {
            "type": "string"
          },
          "creditCost": {
            "type": "string"
          },
          "availableCredits": {
            "type": "string"
          },
          "sufficientCredits": {
            "type": "boolean"
          },
          "subtitleCount": {
            "type": "integer"
          },
          "characterCount": {
            "type": "integer"
          }
        }
      },
      "Credits": {
        "type": "object",
        "properties": {
          "availableCredits": {
            "type": "string",
            "description": "Decimal credits currently available for new tasks."
          },
          "reservedCredits": {
            "type": "string",
            "description": "Decimal credits held for pending/processing tasks, including task-specific holds whose source credits have expired."
          },
          "totalCredits": {
            "type": "string",
            "description": "Decimal credits: availableCredits plus reservedCredits."
          }
        }
      },
      "Capabilities": {
        "type": "object",
        "properties": {
          "modelTiers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "inputFormats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "resultFormats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "srt"
              ]
            }
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          },
          "limits": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          }
        }
      },
      "Progress": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Translation"
          },
          {
            "type": "object",
            "properties": {
              "progress": {
                "type": [
                  "number",
                  "null"
                ],
                "minimum": 0,
                "maximum": 100
              },
              "errorCode": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "requestId"
        ],
        "properties": {
          "requestId": {
            "type": "string"
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {}
            }
          }
        }
      }
    },
    "responses": {
      "Error400": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error401": {
        "description": "Invalid/revoked key or inactive user",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error402": {
        "description": "Insufficient credits",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error404": {
        "description": "Task missing or not owned by this account",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error409": {
        "description": "Idempotency conflict, task not ready, or translation failed",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "description": "Seconds until retry, when applicable.",
            "schema": {
              "type": "integer"
            }
          }
        }
      },
      "Error410": {
        "description": "Idempotent task expired/deleted",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error413": {
        "description": "Request too large",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error415": {
        "description": "Expected JSON",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error422": {
        "description": "Subtitle rejected",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error429": {
        "description": "Account rate or active task limit; honor Retry-After",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "description": "Seconds until retry, when applicable.",
            "schema": {
              "type": "integer"
            }
          }
        }
      },
      "Error500": {
        "description": "Unexpected server error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error502": {
        "description": "Incomplete translation result",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error503": {
        "description": "Queue/model/service temporarily unavailable",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "description": "Seconds until retry, when applicable.",
            "schema": {
              "type": "integer"
            }
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "Integration guide and downloadable Node.js example",
    "url": "/api-docs.html"
  }
}
