{
  "openapi": "3.0.3",
  "info": {
    "title": "Online Font Converter API",
    "description": "Convert font files between supported formats. Discover current input/output capabilities with GET /api/formats; UFO directory uploads are not supported.\n\nUpload up to 25 fonts, 25 MiB per file and 50 MiB per batch. Up to five active jobs per IP address. See GET /api/status for current limits.\n\nSubmit POST /api/convert, then poll GET /api/jobs/{id} or listen to GET /api/jobs/{id}/events. GET /api/jobs/{id}/conversions exposes individual downloadUrl values as fonts finish, before the whole job completes. The existing ZIP/TAR download is available when the job is COMPLETE.\n\nDownloads expire one hour after completion; status metadata remains for 24 hours. Job URLs are private bearer links: anyone with the URL can access that job. Do not publish them. Starting over in the website clears the local workspace, not an API job or its existing links.",
    "version": "1.0.0",
    "contact": {
      "name": "Online Font Converter",
      "url": "https://onlinefontconverter.com"
    }
  },
  "servers": [
    {
      "url": "/",
      "description": "This service"
    }
  ],
  "tags": [
    {
      "name": "status",
      "description": "Service health and capabilities"
    },
    {
      "name": "formats",
      "description": "Supported font formats and conversion matrix"
    },
    {
      "name": "jobs",
      "description": "Font conversion job operations"
    }
  ],
  "paths": {
    "/api/status": {
      "get": {
        "summary": "Get service status",
        "description": "Returns availability, supported formats and upload limits.",
        "operationId": "getStatus",
        "tags": [
          "status"
        ],
        "responses": {
          "200": {
            "description": "Service is healthy and ready",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            }
          },
          "503": {
            "description": "Conversion service temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/formats": {
      "get": {
        "summary": "Get supported formats",
        "description": "Returns all supported font formats and their conversion capabilities",
        "operationId": "getFormats",
        "tags": [
          "formats"
        ],
        "responses": {
          "200": {
            "description": "Supported formats and conversion matrix",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormatsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/convert": {
      "post": {
        "summary": "Submit a font conversion job",
        "description": "Upload font files and specify target formats for conversion.\nReturns a job ID for tracking progress.\n",
        "operationId": "submitConversion",
        "tags": [
          "jobs"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "files",
                  "targetFormats"
                ],
                "properties": {
                  "files": {
                    "type": "array",
                    "description": "Font files to convert; filenames must not contain path separators or control characters",
                    "items": {
                      "type": "string",
                      "format": "binary"
                    }
                  },
                  "targetFormats": {
                    "type": "string",
                    "description": "Comma-separated list of target formats (e.g., \"TTF,WOFF,WOFF2\")",
                    "example": "TTF,WOFF,WOFF2"
                  },
                  "output": {
                    "type": "string",
                    "description": "Archive format for results",
                    "enum": [
                      "zip",
                      "tar"
                    ],
                    "default": "zip"
                  },
                  "sessionId": {
                    "type": "string",
                    "deprecated": true,
                    "description": "Ignored legacy field. Every submission creates a new durable job."
                  },
                  "reuseJobIds": {
                    "type": "string",
                    "maxLength": 4096,
                    "description": "Optional comma-separated list of up to 64 previous job UUIDs, newest first. Upload the complete current selection. Successful outputs with matching source contents, source/target formats and converter recipe are copied into the new job; only missing combinations run. Missing or expired results are cache misses. Job IDs are private bearer capabilities."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job accepted for processing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConvertResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (no files, invalid formats, etc.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "File or batch size exceeds limits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many concurrent jobs for this IP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Upload temporarily unavailable"
          }
        }
      }
    },
    "/api/jobs/{id}": {
      "get": {
        "summary": "Get job status",
        "description": "Returns the current status and progress of a conversion job",
        "operationId": "getJobStatus",
        "tags": [
          "jobs"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID (UUID)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job status retrieved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobStatusResponse"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/jobs/{id}/conversions": {
      "get": {
        "summary": "Get detailed conversion information",
        "description": "Per-font progress and download links. A successful file is available immediately, even while other conversions are running. Links become unavailable after expiry.",
        "operationId": "getJobConversions",
        "tags": [
          "jobs"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID (UUID)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Conversion details retrieved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobConversionsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/jobs/{id}/events": {
      "get": {
        "summary": "Stream job progress events",
        "description": "Server-Sent Events (SSE) stream providing real-time job progress updates",
        "operationId": "getJobEvents",
        "tags": [
          "jobs"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID (UUID)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream of job progress events",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "object",
                  "description": "Stream of JobEvent objects"
                }
              }
            }
          }
        }
      }
    },
    "/api/jobs/{id}/download": {
      "get": {
        "summary": "Download conversion results",
        "description": "Download the archive containing all converted font files",
        "operationId": "downloadJobResults",
        "tags": [
          "jobs"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Job ID (UUID)",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Archive file download",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/x-tar": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Job has not finished"
          },
          "410": {
            "description": "Download has expired"
          },
          "503": {
            "description": "Download temporarily unavailable"
          }
        }
      }
    },
    "/api/jobs/{id}/conversions/{conversionId}/download": {
      "get": {
        "summary": "Download one converted font",
        "description": "Streams a ready font as an attachment. Does not wait for the rest of the batch or its ZIP/TAR archive. Uses the same job expiry as the archive.",
        "operationId": "downloadConvertedFont",
        "tags": [
          "jobs"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "conversionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Font attachment",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Job or conversion not found"
          },
          "409": {
            "description": "Font not ready"
          },
          "410": {
            "description": "Download expired"
          },
          "503": {
            "description": "Download temporarily unavailable"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "StatusResponse": {
        "type": "object",
        "properties": {
          "service": {
            "type": "string",
            "example": "Online Font Converter"
          },
          "version": {
            "type": "string",
            "example": "1.0.0"
          },
          "healthy": {
            "type": "boolean",
            "description": "Whether the service can accept conversions"
          },
          "formats": {
            "$ref": "#/components/schemas/SupportedFormats"
          },
          "limits": {
            "$ref": "#/components/schemas/LimitsInfo"
          },
          "blockedReason": {
            "type": "string",
            "nullable": true,
            "description": "Generic service availability reason, when unavailable"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SupportedFormats": {
        "type": "object",
        "properties": {
          "input": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "output": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "LimitsInfo": {
        "type": "object",
        "properties": {
          "maxFileSize": {
            "type": "integer",
            "format": "int64",
            "description": "Maximum file size in bytes",
            "example": 26214400
          },
          "maxBatchSize": {
            "type": "integer",
            "format": "int64",
            "description": "Maximum total batch size in bytes",
            "example": 52428800
          },
          "maxFilesPerBatch": {
            "type": "integer",
            "description": "Maximum number of files per batch",
            "example": 25
          },
          "maxConcurrentJobsPerIp": {
            "type": "integer",
            "description": "Maximum concurrent jobs per IP address",
            "example": 5
          }
        }
      },
      "FormatsResponse": {
        "type": "object",
        "properties": {
          "formats": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FormatInfo"
            }
          }
        }
      },
      "FormatInfo": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Format code (e.g., \"TTF\", \"WOFF\")"
          },
          "category": {
            "type": "string",
            "description": "Format category (e.g., \"OUTLINE\", \"WEB\")"
          },
          "inputSupported": {
            "type": "boolean",
            "description": "Can be used as input format"
          },
          "outputSupported": {
            "type": "boolean",
            "description": "Can be used as output format"
          },
          "defaultTargets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Recommended default target formats"
          },
          "supportedTargets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "All supported target formats for conversion"
          }
        }
      },
      "ConvertResponse": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "UPLOADING",
              "QUEUED",
              "CONVERTING",
              "PACKAGING",
              "COMPLETE",
              "FAILED",
              "EXPIRED"
            ]
          },
          "message": {
            "type": "string"
          },
          "statusUrl": {
            "type": "string",
            "description": "URL to poll for job status"
          },
          "eventsUrl": {
            "type": "string",
            "description": "URL for SSE stream of job events"
          }
        }
      },
      "JobStatusResponse": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "UPLOADING",
              "QUEUED",
              "CONVERTING",
              "PACKAGING",
              "COMPLETE",
              "FAILED",
              "EXPIRED"
            ]
          },
          "progress": {
            "$ref": "#/components/schemas/JobProgress"
          },
          "submittedAt": {
            "type": "string",
            "format": "date-time"
          },
          "totalConversions": {
            "type": "integer"
          },
          "completedConversions": {
            "type": "integer"
          },
          "successfulConversions": {
            "type": "integer"
          },
          "failedConversions": {
            "type": "integer"
          },
          "skippedConversions": {
            "type": "integer"
          },
          "sourceFileCount": {
            "type": "integer"
          },
          "requestedFormatCount": {
            "type": "integer"
          },
          "sourceFileNames": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "downloadUrl": {
            "type": "string",
            "nullable": true,
            "description": "Download URL (only available when status is COMPLETE)"
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "reusedConversions": {
            "type": "integer",
            "minimum": 0,
            "description": "Successful conversions reused from supplied prior jobs; included in successfulConversions."
          }
        }
      },
      "JobProgress": {
        "type": "object",
        "properties": {
          "uploadPct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "processingPct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "packagePct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "currentPhase": {
            "type": "string"
          }
        }
      },
      "JobConversionsResponse": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "format": "uuid"
          },
          "conversions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConversionDetail"
            }
          }
        }
      },
      "ConversionDetail": {
        "type": "object",
        "properties": {
          "sourceFileId": {
            "type": "string",
            "format": "uuid"
          },
          "sourceName": {
            "type": "string"
          },
          "targetFormat": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "RUNNING",
              "SUCCEEDED",
              "FAILED",
              "SKIPPED"
            ]
          },
          "errorCode": {
            "type": "string",
            "nullable": true
          },
          "errorMessage": {
            "type": "string",
            "nullable": true
          },
          "errorDetails": {
            "type": "string",
            "nullable": true,
            "description": "Deprecated. Internal diagnostics are not exposed.",
            "deprecated": true
          },
          "conversionId": {
            "type": "string",
            "format": "uuid"
          },
          "downloadUrl": {
            "type": "string",
            "nullable": true,
            "description": "Individual font attachment, available once the converted file is ready; null after expiry."
          },
          "filename": {
            "type": "string",
            "nullable": true
          },
          "sizeBytes": {
            "type": "integer",
            "format": "int64",
            "nullable": true
          },
          "sha256": {
            "type": "string",
            "nullable": true,
            "description": "SHA-256 of the converted file, when available."
          },
          "reused": {
            "type": "boolean",
            "default": false,
            "description": "True when the output was reused from a supplied prior job without running a converter."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "nullable": true
          }
        }
      }
    }
  }
}
