{
  "openapi": "3.0.3",
  "info": {
    "title": "402milly.xyz Pixel Marketplace API",
    "description": "API for purchasing and managing pixels on the 402M pixel grid. Powered by x402 protocol for gasless USDC payments on Base, Avalanche, and more.",
    "version": "2.0.0",
    "contact": {
      "name": "Ultravioleta DAO",
      "url": "https://402milly.xyz"
    },
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/licenses/MIT"
    }
  },
  "servers": [
    {
      "url": "https://api.402milly.xyz",
      "description": "Production API (Custom Domain)"
    },
    {
      "url": "https://pmhwtk0d41.execute-api.us-east-1.amazonaws.com/prod",
      "description": "Production API (AWS Gateway URL)"
    }
  ],
  "tags": [
    {
      "name": "Pixels",
      "description": "Pixel purchase and retrieval operations"
    },
    {
      "name": "Tiles",
      "description": "Tile-based pixel loading for 402M grid"
    },
    {
      "name": "Stats",
      "description": "Marketplace statistics and metadata"
    },
    {
      "name": "Upload",
      "description": "Image upload operations"
    }
  ],
  "paths": {
    "/purchase": {
      "post": {
        "tags": ["Pixels"],
        "summary": "Purchase pixels on the grid",
        "description": "Purchase a rectangular block of pixels using x402 protocol (gasless USDC payment). Returns HTTP 402 on first call with payment details, then accepts payment proof via X-PAYMENT header.",
        "operationId": "purchasePixels",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PurchaseRequest"
              },
              "examples": {
                "initialRequest": {
                  "summary": "Initial request (returns 402)",
                  "value": {
                    "x": 100,
                    "y": 200,
                    "width": 100,
                    "height": 100,
                    "title": "My Awesome Project",
                    "linkUrl": "https://example.com",
                    "imageUrl": ""
                  }
                },
                "withPayment": {
                  "summary": "Request with payment proof",
                  "value": {
                    "x": 100,
                    "y": 200,
                    "width": 100,
                    "height": 100,
                    "title": "My Awesome Project",
                    "linkUrl": "https://example.com",
                    "imageUrl": "https://s3.amazonaws.com/ultravioletadao/pixel-marketplace/images/abc123.png"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "X-PAYMENT",
            "in": "header",
            "description": "Base64-encoded x402 payment proof (EIP-712 signature + authorization data)",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Purchase successful",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PurchaseResponse"
                }
              }
            }
          },
          "402": {
            "description": "Payment Required - Returns payment instructions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequired"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (validation error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (pixels already purchased)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/upload": {
      "post": {
        "tags": ["Upload"],
        "summary": "Upload pixel image to S3",
        "description": "Upload a base64-encoded image (PNG, JPEG, GIF, WebP). Image will be resized to match pixel dimensions and uploaded to S3.",
        "operationId": "uploadImage",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UploadRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload successful",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (invalid image format or size)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stats": {
      "get": {
        "tags": ["Stats"],
        "summary": "Get marketplace statistics",
        "description": "Returns aggregated statistics including total pixels sold, revenue, and recent purchases.",
        "operationId": "getStats",
        "responses": {
          "200": {
            "description": "Statistics retrieved successfully",
            "headers": {
              "Cache-Control": {
                "description": "Cache for 2 minutes",
                "schema": {
                  "type": "string",
                  "example": "public, max-age=120"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Stats"
                }
              }
            }
          }
        }
      }
    },
    "/grid/metadata": {
      "get": {
        "tags": ["Stats"],
        "summary": "Get grid metadata",
        "description": "Returns grid configuration including dimensions, tile size, and total capacity.",
        "operationId": "getGridMetadata",
        "responses": {
          "200": {
            "description": "Metadata retrieved successfully",
            "headers": {
              "Cache-Control": {
                "description": "Cache for 1 minute",
                "schema": {
                  "type": "string",
                  "example": "public, max-age=60"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GridMetadata"
                }
              }
            }
          }
        }
      }
    },
    "/tiles/{tileX}/{tileY}/pixels": {
      "get": {
        "tags": ["Tiles"],
        "summary": "Get pixels for a specific tile",
        "description": "Retrieve all pixels within a 1000×1000 tile. Used for viewport virtualization in 402M grid.",
        "operationId": "getTilePixels",
        "parameters": [
          {
            "name": "tileX",
            "in": "path",
            "description": "Tile X coordinate (0-19)",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 19
            }
          },
          {
            "name": "tileY",
            "in": "path",
            "description": "Tile Y coordinate (0-20)",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tile pixels retrieved successfully",
            "headers": {
              "Cache-Control": {
                "description": "Cache for 1 hour",
                "schema": {
                  "type": "string",
                  "example": "public, max-age=3600"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TilePixels"
                }
              }
            }
          },
          "400": {
            "description": "Invalid tile coordinates",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tiles/batch": {
      "post": {
        "tags": ["Tiles"],
        "summary": "Get pixels for multiple tiles in parallel",
        "description": "Fetch up to 300 tiles in a single request. Returns pixels grouped by tile.",
        "operationId": "getTilesBatch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TilesBatchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tiles retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TilesBatchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request (too many tiles or invalid coordinates)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/pixels/owned/{walletAddress}": {
      "get": {
        "tags": ["Pixels"],
        "summary": "Get pixels owned by a wallet address",
        "description": "Retrieve all pixel purchases made by a specific wallet address.",
        "operationId": "getPixelsByOwner",
        "parameters": [
          {
            "name": "walletAddress",
            "in": "path",
            "description": "Ethereum wallet address (0x...)",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^0x[a-fA-F0-9]{40}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Owned pixels retrieved successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OwnedPixels"
                }
              }
            }
          },
          "400": {
            "description": "Invalid wallet address",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PurchaseRequest": {
        "type": "object",
        "required": ["x", "y", "width", "height", "title", "linkUrl"],
        "properties": {
          "x": {
            "type": "integer",
            "description": "Top-left X coordinate",
            "minimum": 0,
            "maximum": 20049,
            "example": 100
          },
          "y": {
            "type": "integer",
            "description": "Top-left Y coordinate",
            "minimum": 0,
            "maximum": 20049,
            "example": 200
          },
          "width": {
            "type": "integer",
            "description": "Block width in pixels",
            "minimum": 10,
            "maximum": 20050,
            "example": 100
          },
          "height": {
            "type": "integer",
            "description": "Block height in pixels",
            "minimum": 10,
            "maximum": 20050,
            "example": 100
          },
          "title": {
            "type": "string",
            "description": "Title for this pixel block",
            "maxLength": 100,
            "example": "My Awesome Project"
          },
          "linkUrl": {
            "type": "string",
            "description": "URL to link to (optional)",
            "format": "uri",
            "example": "https://example.com"
          },
          "imageUrl": {
            "type": "string",
            "description": "S3 URL of uploaded image (empty on initial 402 request)",
            "format": "uri",
            "example": "https://s3.amazonaws.com/ultravioletadao/pixel-marketplace/images/abc123.png"
          }
        }
      },
      "PurchaseResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "example": "Purchase successful"
          },
          "purchaseId": {
            "type": "string",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000"
          },
          "pixels": {
            "type": "integer",
            "example": 10000
          },
          "priceUSD": {
            "type": "number",
            "format": "float",
            "example": 100.00
          }
        }
      },
      "PaymentRequired": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "Payment required"
          },
          "payment": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "string",
                "description": "Amount in USDC (6 decimals)",
                "example": "100000000"
              },
              "token": {
                "type": "string",
                "description": "USDC contract address",
                "example": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
              },
              "recipient": {
                "type": "string",
                "description": "Wallet address to receive payment",
                "example": "0x1234567890123456789012345678901234567890"
              },
              "network": {
                "type": "string",
                "description": "Chain ID or network name",
                "example": "base"
              },
              "supportedChains": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "example": ["base", "avalanche", "ethereum"]
              }
            }
          }
        }
      },
      "UploadRequest": {
        "type": "object",
        "required": ["image", "width", "height"],
        "properties": {
          "image": {
            "type": "string",
            "description": "Base64-encoded image (PNG, JPEG, GIF, WebP)",
            "example": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..."
          },
          "width": {
            "type": "integer",
            "description": "Target width in pixels",
            "example": 100
          },
          "height": {
            "type": "integer",
            "description": "Target height in pixels",
            "example": 100
          }
        }
      },
      "UploadResponse": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "example": "https://s3.amazonaws.com/ultravioletadao/pixel-marketplace/images/abc123.png"
          }
        }
      },
      "Stats": {
        "type": "object",
        "properties": {
          "totalPixelsSold": {
            "type": "number",
            "example": 20764.0
          },
          "totalPixelsAvailable": {
            "type": "number",
            "example": 401979236.0
          },
          "totalRevenue": {
            "type": "number",
            "format": "float",
            "example": 1197.64
          },
          "percentageSold": {
            "type": "number",
            "format": "float",
            "example": 0.0052
          },
          "totalPurchases": {
            "type": "integer",
            "example": 89
          },
          "recentPurchases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Purchase"
            }
          }
        }
      },
      "Purchase": {
        "type": "object",
        "properties": {
          "purchaseId": {
            "type": "string",
            "format": "uuid"
          },
          "pixels": {
            "type": "number"
          },
          "priceUSD": {
            "type": "number",
            "format": "float"
          },
          "timestamp": {
            "type": "integer",
            "description": "Unix timestamp"
          },
          "title": {
            "type": "string"
          },
          "x": {
            "type": "integer"
          },
          "y": {
            "type": "integer"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "imageUrl": {
            "type": "string",
            "format": "uri"
          },
          "linkUrl": {
            "type": "string",
            "format": "uri"
          },
          "owner": {
            "type": "string",
            "description": "Wallet address"
          }
        }
      },
      "GridMetadata": {
        "type": "object",
        "properties": {
          "gridWidth": {
            "type": "integer",
            "example": 20050
          },
          "gridHeight": {
            "type": "integer",
            "example": 20050
          },
          "tileSize": {
            "type": "integer",
            "example": 1000
          },
          "tilesX": {
            "type": "integer",
            "example": 20
          },
          "tilesY": {
            "type": "integer",
            "example": 21
          },
          "totalPixels": {
            "type": "integer",
            "example": 402000000
          },
          "pricePerPixel": {
            "type": "number",
            "format": "float",
            "example": 0.01
          }
        }
      },
      "TilePixels": {
        "type": "object",
        "properties": {
          "tileX": {
            "type": "integer"
          },
          "tileY": {
            "type": "integer"
          },
          "pixels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Purchase"
            }
          }
        }
      },
      "TilesBatchRequest": {
        "type": "object",
        "required": ["tiles"],
        "properties": {
          "tiles": {
            "type": "array",
            "maxItems": 300,
            "items": {
              "type": "object",
              "required": ["tileX", "tileY"],
              "properties": {
                "tileX": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 19
                },
                "tileY": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 20
                }
              }
            },
            "example": [
              {"tileX": 0, "tileY": 0},
              {"tileX": 1, "tileY": 0}
            ]
          }
        }
      },
      "TilesBatchResponse": {
        "type": "object",
        "properties": {
          "tiles": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Purchase"
              }
            },
            "example": {
              "0_0": [],
              "1_0": []
            }
          }
        }
      },
      "OwnedPixels": {
        "type": "object",
        "properties": {
          "owner": {
            "type": "string"
          },
          "totalPurchases": {
            "type": "integer"
          },
          "totalPixels": {
            "type": "number"
          },
          "purchases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Purchase"
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "example": "Invalid request"
          },
          "message": {
            "type": "string",
            "example": "Detailed error message"
          }
        }
      }
    }
  }
}
