{
  "openapi": "3.1.0",
  "info": {
    "title": "MoonPush API",
    "version": "1.0.0",
    "description": "Free temporary file sharing API built for AI agents, LLMs, automation scripts, and developers. Upload any file with no signup. Files auto-delete after 30 minutes. End-to-end encrypted by default.",
    "contact": {
      "name": "MoonPush",
      "url": "https://www.moonpush.com",
      "email": "aamirmursleen@gmail.com"
    },
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    { "url": "https://www.moonpush.com", "description": "Production" }
  ],
  "externalDocs": {
    "description": "Full markdown reference for LLMs",
    "url": "https://www.moonpush.com/llm.md"
  },
  "paths": {
    "/api/upload": {
      "post": {
        "summary": "Upload a file (one-shot)",
        "description": "Server-side ephemeral encryption: the server generates a one-time AES-GCM 256 key, encrypts the file, returns the key in the response, and never stores it. The shareUrl includes the key in the URL fragment (#KEY) which is never sent back to the server.",
        "operationId": "uploadFile",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": {
                  "file": { "type": "string", "format": "binary", "description": "The file to upload (any size up to 100 GB)" },
                  "plain": { "type": "string", "enum": ["true"], "description": "Set to 'true' to upload without server-side encryption" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload accepted",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UploadResponse" }
              }
            }
          },
          "413": { "description": "File too large" },
          "429": { "description": "Rate limited" }
        }
      }
    },
    "/api/pipe/new": {
      "post": {
        "summary": "Create a pipe (instant link, before upload)",
        "description": "Returns a presigned R2 URL for direct browser→R2 upload. Used by the chunked client uploader so a shareable link is available before the upload completes.",
        "operationId": "createPipe",
        "parameters": [
          { "name": "x-file-name", "in": "header", "required": true, "schema": { "type": "string" } },
          { "name": "x-file-type", "in": "header", "schema": { "type": "string" } },
          { "name": "x-expiry-minutes", "in": "header", "schema": { "type": "integer", "default": 30 } },
          { "name": "x-encrypted", "in": "header", "schema": { "type": "string", "enum": ["true"] } }
        ],
        "responses": {
          "201": {
            "description": "Pipe created",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PipeNewResponse" } } }
          }
        }
      }
    },
    "/api/dl/{id}": {
      "get": {
        "summary": "Download a file by UUID",
        "description": "Streams the file. For server-encrypted uploads, the server decrypts on the fly and returns plaintext.",
        "operationId": "downloadFile",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "File stream", "content": { "application/octet-stream": {} } },
          "404": { "description": "File not found or expired" }
        }
      }
    },
    "/api/pipe/{id}": {
      "delete": {
        "summary": "Revoke a file before expiry",
        "description": "Owner-initiated immediate deletion. Requires the deleteToken returned from /api/pipe/new (or from /api/upload).",
        "operationId": "deletePipe",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "x-delete-token", "in": "header", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Deleted (or already gone)", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string", "enum": ["deleted", "gone"] } } } } } },
          "403": { "description": "Wrong or missing delete token" }
        }
      }
    },
    "/api/health": {
      "get": {
        "summary": "Health check",
        "operationId": "health",
        "responses": {
          "200": {
            "description": "Service healthy",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string", "enum": ["ok"] }, "service": { "type": "string" }, "timestamp": { "type": "string", "format": "date-time" } } } } }
          }
        }
      }
    },
    "/share/{code}": {
      "get": {
        "summary": "Human-friendly share page",
        "description": "Renders an HTML page with file preview, QR code, and download button. The encryption key lives in the URL fragment (#KEY) — never sent to the server.",
        "operationId": "sharePage",
        "parameters": [
          { "name": "code", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "HTML page" } }
      }
    }
  },
  "components": {
    "schemas": {
      "UploadResponse": {
        "type": "object",
        "required": ["id", "shortCode", "shareUrl", "downloadUrl"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "shortCode": { "type": "string" },
          "originalName": { "type": "string" },
          "mimeType": { "type": "string" },
          "size": { "type": "integer" },
          "uploadedAt": { "type": "string", "format": "date-time" },
          "encrypted": { "type": "boolean" },
          "shareUrl": { "type": "string", "format": "uri", "description": "Human-shareable URL with the encryption key in the URL fragment" },
          "downloadUrl": { "type": "string", "format": "uri", "description": "Direct API download URL" },
          "directUrl": { "type": "string", "format": "uri", "description": "CDN URL (only useful for unencrypted files)" },
          "deleteToken": { "type": "string", "description": "Opaque token required to DELETE this file before expiry" }
        }
      },
      "PipeNewResponse": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "shortCode": { "type": "string" },
          "pipeUrl": { "type": "string" },
          "receiveUrl": { "type": "string" },
          "presignedUploadUrl": { "type": "string", "description": "Presigned R2 PUT URL valid for 1 hour" },
          "expiryMinutes": { "type": "integer" },
          "expiresAt": { "type": "string", "format": "date-time" },
          "deleteToken": { "type": "string" }
        }
      }
    }
  }
}
