{
  "openapi": "3.1.0",
  "info": {
    "title": "Quiet Ox File Sharing API",
    "description": "Ephemeral AES-256-GCM encrypted file sharing. Files self-delete after 1–14 days. Upload a file and receive a shareable link plus a decryption key. The key is returned once and never stored server-side.",
    "version": "1.0.0",
    "contact": {
      "email": "privacy@quietox.com"
    }
  },
  "servers": [
    {
      "url": "https://quietox.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/v1/upload": {
      "post": {
        "operationId": "uploadFile",
        "summary": "Upload and encrypt a file",
        "description": "Encrypts the file server-side with AES-256-GCM and stores it ephemerally. Returns shareUrl (browser link with decryption key in URL #fragment, for human recipients) and decryptionKey (for API re-download). Save the decryptionKey — it is never retained by the server.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "File to upload. Maximum 75 MB."
                  },
                  "lifetime": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 14,
                    "default": 7,
                    "description": "Lifetime in days (1–14). File is permanently deleted after this period."
                  },
                  "filename": {
                    "type": "string",
                    "description": "Optional filename override."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "File uploaded and encrypted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fileId": {
                      "type": "string"
                    },
                    "shareUrl": {
                      "type": "string",
                      "description": "Browser link with #decryptionKey fragment. Share with human recipients."
                    },
                    "downloadUrl": {
                      "type": "string",
                      "description": "API download endpoint for this file."
                    },
                    "decryptionKey": {
                      "type": "string",
                      "description": "Base64url AES-256-GCM key. Not stored server-side — save this."
                    },
                    "expiresAt": {
                      "type": "integer",
                      "description": "Unix timestamp when the file is permanently deleted."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Unauthorized"
          },
          "413": {
            "description": "File exceeds 75 MB limit"
          }
        }
      }
    },
    "/api/v1/files/{fileId}": {
      "get": {
        "operationId": "getFileMeta",
        "summary": "Get file metadata",
        "description": "Returns metadata for an active file. Returns 404 if the file has expired or been deleted.",
        "parameters": [
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "File metadata",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fileId": {
                      "type": "string"
                    },
                    "originalName": {
                      "type": "string"
                    },
                    "contentType": {
                      "type": "string"
                    },
                    "sizeBytes": {
                      "type": "integer"
                    },
                    "shareMode": {
                      "type": "string",
                      "enum": [
                        "link",
                        "email"
                      ]
                    },
                    "expiresAt": {
                      "type": "integer"
                    },
                    "downloadCount": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "404": {
            "description": "File not found or expired"
          }
        }
      }
    },
    "/api/v1/files/{fileId}/download": {
      "get": {
        "operationId": "downloadFile",
        "summary": "Download and decrypt a file",
        "description": "Decrypts and returns the raw file using the decryptionKey from the upload response. Only available for link-share files — email-share files require the browser verification flow.",
        "parameters": [
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Decryption-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Base64url AES-256-GCM key from the upload response."
          }
        ],
        "responses": {
          "200": {
            "description": "Raw decrypted file bytes",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Missing or malformed X-Decryption-Key"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Email-share file — not accessible via API"
          },
          "404": {
            "description": "File not found or expired"
          },
          "422": {
            "description": "Decryption failed — key incorrect or file corrupted"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Set via: wrangler pages secret put API_KEY"
      }
    }
  }
}