{
  "openapi": "3.0.3",
  "info": {
    "title": "Presend API",
    "description": "Free utility API: file hashing, UUID generation, Base64 encoding, JWT decoding, URL tracking-parameter cleaning, and IP geolocation. No authentication required.",
    "version": "1.0.0",
    "contact": {
      "url": "https://presend.pages.dev/api"
    },
    "license": {
      "name": "MIT",
      "url": "https://github.com/presendapp/presend/blob/main/LICENSE"
    }
  },
  "servers": [
    {
      "url": "https://presend.pages.dev/api",
      "description": "Production"
    }
  ],
  "paths": {
    "/hash": {
      "post": {
        "summary": "Hash a file",
        "description": "Returns SHA-256, SHA-1 and SHA-512 for a file up to 20MB.",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Hash results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "size_bytes": {
                      "type": "integer"
                    },
                    "hashes": {
                      "type": "object",
                      "properties": {
                        "sha256": {
                          "type": "string"
                        },
                        "sha1": {
                          "type": "string"
                        },
                        "sha512": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "File too large"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/url-clean": {
      "get": {
        "summary": "Clean a single URL",
        "description": "Strips 60+ tracking parameters from a URL.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cleaned URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "original": {
                      "type": "string"
                    },
                    "clean": {
                      "type": "string"
                    },
                    "removed": {
                      "type": "integer"
                    },
                    "valid": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      },
      "post": {
        "summary": "Clean a batch of URLs",
        "description": "Strips tracking parameters from up to 100 URLs at once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "urls": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 100
                  }
                },
                "required": [
                  "urls"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cleaned URLs batch"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/uuid": {
      "get": {
        "summary": "Generate UUIDs",
        "description": "Generates up to 100 random UUID v4 values.",
        "parameters": [
          {
            "name": "count",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Generated UUIDs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "uuids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/base64": {
      "get": {
        "summary": "Encode or decode Base64",
        "parameters": [
          {
            "name": "action",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "encode",
                "decode"
              ]
            }
          },
          {
            "name": "text",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 10000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Encoded/decoded result"
          },
          "400": {
            "description": "Invalid input"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/jwt-decode": {
      "get": {
        "summary": "Decode a JWT",
        "description": "Decodes the header and payload of a JWT. Does not verify the signature.",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Decoded JWT",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "header": {
                      "type": "object"
                    },
                    "payload": {
                      "type": "object"
                    },
                    "signature_verified": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid JWT"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/ip": {
      "get": {
        "summary": "Get caller IP geolocation",
        "description": "Returns the caller's IP with city, country, currency, language, EU and VPN flags.",
        "responses": {
          "200": {
            "description": "IP geolocation data"
          }
        }
      }
    },
    "/timestamp": {
      "get": {
        "summary": "Convert timestamps",
        "description": "Current time, or convert between Unix timestamp and ISO date.",
        "parameters": [
          {
            "name": "unix",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Converted timestamp"
          }
        }
      }
    },
    "/color": {
      "get": {
        "summary": "Convert color formats",
        "description": "Convert between hex, RGB and HSL.",
        "parameters": [
          {
            "name": "hex",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rgb",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hsl",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Converted color"
          }
        }
      }
    },
    "/user-agent": {
      "get": {
        "summary": "Parse a User-Agent string",
        "parameters": [
          {
            "name": "ua",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Parsed User-Agent"
          }
        }
      }
    },
    "/password": {
      "get": {
        "summary": "Generate a secure password",
        "parameters": [
          {
            "name": "length",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 16,
              "maximum": 128
            }
          },
          {
            "name": "symbols",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            }
          },
          {
            "name": "uppercase",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            }
          },
          {
            "name": "numbers",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            }
          },
          {
            "name": "exclude_ambiguous",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "0",
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Generated password with entropy estimate"
          }
        }
      }
    },
    "/email-validate": {
      "get": {
        "summary": "Validate an email address",
        "description": "Checks syntax and verifies the domain has a valid MX record.",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Validation result"
          }
        }
      }
    },
    "/favicon": {
      "get": {
        "summary": "Fetch a domain's favicon URL",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Favicon URL"
          },
          "400": {
            "description": "Disallowed domain"
          }
        }
      }
    },
    "/csv-json": {
      "get": {
        "summary": "Convert between JSON and CSV (GET)",
        "parameters": [
          {
            "name": "direction",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "json-to-csv",
                "csv-to-json"
              ]
            }
          },
          {
            "name": "data",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Converted data"
          }
        }
      },
      "post": {
        "summary": "Convert between JSON and CSV (POST)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "direction": {
                    "type": "string",
                    "enum": [
                      "json-to-csv",
                      "csv-to-json"
                    ]
                  },
                  "data": {
                    "type": "string"
                  }
                },
                "required": [
                  "direction",
                  "data"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Converted data"
          }
        }
      }
    },
    "/password-breach": {
      "get": {
        "summary": "Check if a password has been breached",
        "description": "Uses HIBP k-anonymity \u2014 only 5 hash chars are sent externally.",
        "parameters": [
          {
            "name": "password",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Breach status"
          }
        }
      }
    },
    "/email-disposable": {
      "get": {
        "summary": "Detect disposable email domains",
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Disposable status"
          }
        }
      }
    },
    "/security-headers": {
      "get": {
        "summary": "Audit HTTP security headers",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Security grade and per-header breakdown"
          }
        }
      }
    },
    "/url-reputation": {
      "get": {
        "summary": "Check URL against malware/phishing database",
        "description": "Uses URLhaus (abuse.ch).",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reputation result"
          }
        }
      }
    },
    "/subdomains": {
      "get": {
        "summary": "Passive subdomain discovery",
        "description": "Via Certificate Transparency logs (crt.sh). Depends on a third-party service with occasional downtime.",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of discovered subdomains"
          }
        }
      }
    }
  }
}