{
  "openapi": "3.0.4",
  "info": {
    "title": "BillboardBug Public API",
    "description": "The BillboardBug Public API lets you manage your deals, posts, and storefronts programmatically.\nYou can also use the API to generate AI images — useful for creating deal artwork without a design tool.\n\n## Getting Started\n1. **Create an account** at [https://app.billboardbug.com](https://app.billboardbug.com) if you don't have one already.\n2. Log in and navigate to **Settings → API Keys** to generate a key.\n3. Pass the key in the `X-Api-Key` header on every request.\n\nAll content created or modified through the API is associated with the account that owns the key.\nIf that account is suspended the key will be rejected.\n\n## Creating a Deal\n\n**Step 0 — (Optional) Generate an image with AI**\n\nUse the image generation endpoint to create deal artwork from a text prompt — no design tool needed.\nGrok is recommended for its flexible aspect ratio support.\n\n```\nPOST /ai/image\nX-Api-Key: <your-key>\nX-Version: 1\nContent-Type: application/json\n```\n```json\n{\n  \"prompt\": \"A bright summer sale banner for a clothing store, bold colors\",\n  \"aiProvider\": 5,\n  \"width\": 640,\n  \"height\": 373\n}\n```\n\nThe response contains a `base64Data` field (`data:image/jpeg;base64,...`) which you can convert\nto a file and upload in Step 1. See **POST /ai/image** for provider options and resolution details.\n\n**Step 1 — Upload an image or video**\n\nUpload an image (recommended 640 × 373 px JPEG) using category `billboard-image`, or a video (640 × 373 px) using category `billboard-video`. Save the `id` from the response and pass it as `imageId` or `videoId` in the deal request.\n\n```\nPOST /blobs?category=billboard-image\nX-Api-Key: <your-key>\nX-Version: 3\nContent-Type: multipart/form-data\n\nfile=<binary>\n```\n\nResponse:\n```json\n{ \"id\": \"abc123.jpg\" }\n```\n\n**Step 2 — Create the deal**\n\nPass the blob `id` as `imageId`. Hours use `HH:MM` (24-hour). Omit `altitudeMeters` for a standard pole mount.\n\n```\nPOST /business/quick-create-deal\nX-Api-Key: <your-key>\nX-Version: 1\nContent-Type: application/json\n```\n```json\n{\n  \"title\": \"Summer Sale\",\n  \"description\": \"20% off this weekend.\",\n  \"imageId\": \"abc123.jpg\",\n  \"name\": \"My Store\",\n  \"locationDescription\": \"123 Main St, Springfield, IL\",\n  \"locationLat\": 39.7817,\n  \"locationLng\": -89.6501,\n  \"mondayEnabled\": true, \"monday24Hours\": false,\n  \"mondayOpen\": \"09:00\", \"mondayClose\": \"17:00\",\n  \"tuesdayEnabled\": true, \"tuesday24Hours\": false,\n  \"tuesdayOpen\": \"09:00\", \"tuesdayClose\": \"17:00\",\n  \"wednesdayEnabled\": true, \"wednesday24Hours\": false,\n  \"wednesdayOpen\": \"09:00\", \"wednesdayClose\": \"17:00\",\n  \"thursdayEnabled\": true, \"thursday24Hours\": false,\n  \"thursdayOpen\": \"09:00\", \"thursdayClose\": \"17:00\",\n  \"fridayEnabled\": true, \"friday24Hours\": false,\n  \"fridayOpen\": \"09:00\", \"fridayClose\": \"17:00\",\n  \"saturdayEnabled\": true, \"saturday24Hours\": false,\n  \"saturdayOpen\": \"09:00\", \"saturdayClose\": \"17:00\",\n  \"sundayEnabled\": true, \"sunday24Hours\": false,\n  \"sundayOpen\": \"09:00\", \"sundayClose\": \"17:00\",\n  \"isEnabled\": true\n}\n```\n\nThe response contains `storefrontId` and `billboardId` — save these to update the deal later.\n\n**Featured deals (optional)**\n\nSetting `isFeatured: true` on a deal promotes it and incurs a daily cost. The per-day rate is returned by\n`GET /global-config/all` as `billboardDayAdCost`, which is the dollar amount × 100 with no cents\n(e.g. `500` = $5.00/day). Discount options are available for purchase under Business → Discounts.\nYour account must have a billing method on file to set a deal as featured.\n\n**Step 3 — Update the deal**\n\nUse the same body shape as create.\n\n```\nPUT /business/quick-update-deal/{storefrontId}/{billboardId}\nX-Api-Key: <your-key>\nX-Version: 1\nContent-Type: application/json\n```\n\n## Creating a Post\n\nPosts are standalone pieces of content (text, image, or video) attached to your account.\nTo comment on an existing post supply `parentPostId`.\n\n**Step 1 — (Optional) Upload an image or video**\n\nUpload an image (recommended width: 640 px, height varies) using category `post-image`, or a video using category `post-video`. Save the `id` and pass it as `imageId` or `videoId` in the post request.\n\n```\nPOST /blobs?category=post-image\nX-Api-Key: <your-key>\nX-Version: 3\nContent-Type: multipart/form-data\n\nfile=<binary>\n```\n\nResponse:\n```json\n{ \"id\": \"abc123.jpg\" }\n```\n\n**Step 2 — Create the post**\n\n```\nPOST /posts\nX-Api-Key: <your-key>\nX-Version: 1\nContent-Type: application/json\n```\n```json\n{\n  \"text\": \"Check out our summer sale! #sale #deals\",\n  \"imageId\": \"abc123.jpg\",\n  \"isActive\": true,\n  \"isMature\": false\n}\n```\n\nThe response contains the post `id`. Use it to edit or delete the post later.\n\n**Step 3 — Edit the post**\n\n```\nPUT /posts/{id}\nX-Api-Key: <your-key>\nX-Version: 1\nContent-Type: application/json\n```\n```json\n{\n  \"text\": \"Updated caption! #sale\",\n  \"imageId\": \"abc123.jpg\",\n  \"isActive\": true,\n  \"isMature\": false\n}\n```\n\n**Step 4 — Delete the post**\n\n```\nDELETE /posts/{id}\nX-Api-Key: <your-key>\nX-Version: 1\n```\n",
    "version": "1.0.0"
  },
  "paths": {
    "/ai/image": {
      "post": {
        "tags": [
          "Ai"
        ],
        "summary": "Generate an image from a text prompt.",
        "description": "Generates one or more images using an AI image model.\n            \n**Supported providers** (via `aiProvider` field):\n| Value | Provider |\n|-------|----------|\n| `1` | OpenAI (DALL-E 3) |\n| `5` | Grok ⭐ Recommended |\n            \n**Grok is recommended** for image generation.\n            \n### Width & Height\n            \nBoth `width` and `height` are optional. Behavior depends on the provider:\n            \n**Grok** — width/height are used to determine the closest supported aspect ratio.\nThe exact pixel values are not used directly; the ratio is snapped to the nearest\nvalue from Grok's supported set:\n`1:1`, `4:3`, `3:4`, `16:9`, `9:16`, `3:2`, `2:3`, `2:1`, `1:2`, `20:9`, `9:20`, `19.5:9`, `9:19.5`.\nResolution is set to `2k` when either dimension is ≥ 1500px, otherwise `1k`.\nIf omitted, Grok auto-selects the best ratio for the prompt.\n            \n**OpenAI (DALL-E 3)** — only three fixed output sizes are supported:\n- Square (or omitted) → 1024×1024\n- Landscape (width > height) → 1792×1024\n- Portrait (height > width) → 1024×1792\n            \nReturns an array of images as base64-encoded JPEG data URLs.\n            \n**Example request:**\n```json\n{\n  \"prompt\": \"A sunset over a mountain range, photorealistic\",\n  \"aiProvider\": 5,\n  \"width\": 1280,\n  \"height\": 720\n}\n```\n            \n**Example response:**\n```json\n[\n  {\n    \"base64Data\": \"data:image/jpeg;base64,/9j/4AAQSkZJRgAB...\",\n    \"url\": null,\n    \"aiProvider\": 5\n  }\n]\n```",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AiImageRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/AiImageRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/AiImageRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Array of generated images."
          }
        }
      }
    },
    "/blobs": {
      "head": {
        "tags": [
          "Blob"
        ],
        "summary": "Check whether a blob exists.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The blob ID returned from a previous upload (e.g. `abc123.jpg`).",
            "style": "form",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Storage category. One of: `billboard-image`, `billboard-video`, `storefront-image`, `post-image`, `post-video`, `profile-picture`, `profile-banner`.",
            "style": "form",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Blob exists."
          },
          "404": {
            "description": "Blob not found."
          }
        }
      },
      "get": {
        "tags": [
          "Blob"
        ],
        "summary": "Download a blob (image or video).",
        "description": "Returns the raw file with the correct `Content-Type` header. Supports HTTP range requests for partial content (video streaming).\n            \nUse the `id` returned from the upload endpoint as-is. The `category` must match the one used at upload time.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The blob ID returned from a previous upload.",
            "style": "form",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Storage category. One of: `billboard-image`, `billboard-video`, `storefront-image`, `post-image`, `post-video`, `profile-picture`, `profile-banner`.",
            "style": "form",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Full file stream."
          },
          "206": {
            "description": "Partial file stream (range request)."
          },
          "404": {
            "description": "Blob not found."
          }
        }
      },
      "post": {
        "tags": [
          "Blob"
        ],
        "summary": "Upload a file (image or video).",
        "description": "Upload a file as `multipart/form-data`. On success the response contains an `id` string.\n            \n**Workflow:**\n1. Upload the file here → receive `{ \"id\": \"...\" }`\n2. Pass that `id` as the `imageId` or `videoId` field when calling\n   `POST /business/quick-create-deal` or `PUT /business/quick-update-deal/{storefrontId}/{billboardId}`\n            \n**Category values:**\n            \n| Value | Use for | Recommended size |\n|---|---|---|\n| `billboard-image` | Deal/billboard image (`imageId`) | 640 × 373 px |\n| `billboard-video` | Deal/billboard video (`videoId`) | 640 × 373 px |\n| `storefront-image` | Storefront (business) profile image | — |\n| `post-image` | Post image | — |\n| `post-video` | Post video | — |\n| `profile-picture` | Account profile photo | — |\n| `profile-banner` | Account cover/banner photo | — |",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "description": "Storage category (see table above).",
            "style": "form",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "description": "The file to upload (multipart/form-data).",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "file": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Returns `{ \"id\": \"<blob-id>\" }` — store this and use it as the `imageId` or `videoId` on deals.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "type": "string"
                }
              },
              "text/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "No file provided or file is empty."
          }
        }
      },
      "delete": {
        "tags": [
          "Blob"
        ],
        "summary": "Delete a blob.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "description": "The blob ID to delete.",
            "style": "form",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Storage category the blob belongs to. One of: `billboard-image`, `billboard-video`, `storefront-image`, `post-image`, `post-video`, `profile-picture`, `profile-banner`.",
            "style": "form",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Blob deleted successfully."
          }
        }
      }
    },
    "/business/storefront-categories": {
      "get": {
        "tags": [
          "Business"
        ],
        "summary": "List all storefront categories.",
        "description": "Returns the full list of categories available for use when creating or updating a deal.\nPass the category `id` as the `categoryId` field in create/update deal requests.",
        "responses": {
          "200": {
            "description": "Array of categories.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StorefrontCategoryResponse"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StorefrontCategoryResponse"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/StorefrontCategoryResponse"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/business/quick-create-deal": {
      "post": {
        "tags": [
          "Business"
        ],
        "summary": "Create a new deal.",
        "description": "Creates a storefront (business location) and a billboard (the deal/ad) in a single request.\nThe returned `storefrontId` and `billboardId` can be used to update the deal later.\n            \nTo attach an image or video, first upload it via `POST /blobs` with category `billboard-image`\nor `billboard-video`, then pass the returned `id` as `imageId` or `videoId` here.\n            \n**`altitudeMeters`** — offset from the user's ground level. Omit (or pass `null`) to place the billboard\non a standard pole. When specified, the billboard floats that many meters above the ground at the given location.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuickUpdateDealRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/QuickUpdateDealRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/QuickUpdateDealRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The created deal.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              }
            }
          },
          "400": {
            "description": "Validation error."
          }
        }
      }
    },
    "/business/quick-get-deals": {
      "get": {
        "tags": [
          "Business"
        ],
        "summary": "List your deals.",
        "description": "Returns a paginated list of all deals belonging to the authenticated account.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Max results to return (1–100, default 25).",
            "style": "form",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 25
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of results to skip for pagination (default 0).",
            "style": "form",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of deals.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DealResponse"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DealResponse"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DealResponse"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/business/quick-update-deal/{storefrontId}/{billboardId}": {
      "put": {
        "tags": [
          "Business"
        ],
        "summary": "Update an existing deal.",
        "description": "Updates the storefront and billboard identified by the given IDs. Both IDs are returned\nwhen a deal is created via `POST /business/quick-create-deal` or listed via `GET /business/quick-get-deals`.\n            \nTo update the image or video, upload a new file via `POST /blobs` first and pass the\nnew blob `id` as `imageId` or `videoId`.",
        "parameters": [
          {
            "name": "storefrontId",
            "in": "path",
            "description": "The storefront ID of the deal to update.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "billboardId",
            "in": "path",
            "description": "The billboard ID of the deal to update.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "description": "The updated deal fields.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuickUpdateDealRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/QuickUpdateDealRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/QuickUpdateDealRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated deal.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              }
            }
          },
          "400": {
            "description": "Validation error."
          }
        }
      }
    },
    "/business/deals/search": {
      "get": {
        "tags": [
          "Business"
        ],
        "summary": "Search deals",
        "description": "Returns deals matching the given search term. Requires authentication.\nThe `search` parameter must be at least 3 characters.\n            \n**Example request:**\n```\nGET /business/deals/search?search=pizza&limit=10&offset=0\n```\n            \n**Example response:**\n```json\n[\n  {\n    \"accountId\": \"a1b2c3d4-0000-0000-0000-000000000000\",\n    \"billboardId\": \"a1b2c3d4-0000-0000-0000-000000000001\",\n    \"storefrontId\": \"a1b2c3d4-0000-0000-0000-000000000002\",\n    \"title\": \"50% off all pizzas tonight\",\n    \"description\": \"Dine-in only. Valid until 10pm.\",\n    \"created\": \"2024-01-01T00:00:00Z\",\n    \"updated\": \"2024-01-02T00:00:00Z\",\n    \"configureStart\": false,\n    \"start\": null,\n    \"configureEnd\": false,\n    \"end\": null,\n    \"storefrontName\": \"Mario's Pizza\",\n    \"locationDescription\": \"123 Main St, Springfield\",\n    \"locationLat\": 37.7749,\n    \"locationLng\": -122.4194,\n    \"altitudeMeters\": null,\n    \"categoryId\": \"a1b2c3d4-0000-0000-0000-000000000003\",\n    \"isFavorited\": false,\n    \"isReported\": false,\n    \"storefrontImageId\": \"img_abc123\",\n    \"billboardImageId\": \"img_def456\",\n    \"billboardVideoId\": null,\n    \"isEnabled\": true,\n    \"isFeatured\": true,\n    \"unfeatureAt\": null,\n    \"publicApi\": true\n  }\n]\n```",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Max results to return (1–100, default 25).",
            "style": "form",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of results to skip for pagination (default 0).",
            "style": "form",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Search term (minimum 3 characters).",
            "style": "form",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of matching deals.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DealResponse"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DealResponse"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DealResponse"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Search term must be at least 3 characters."
          }
        }
      }
    },
    "/business/deal/{storefrontId}/{billboardId}": {
      "get": {
        "tags": [
          "Business"
        ],
        "summary": "Get deal details",
        "description": "Returns full details for a single deal by storefront and billboard ID. Does not require authentication.\n            \n**Example request:**\n```\nGET /business/deal/a1b2c3d4-0000-0000-0000-000000000002/a1b2c3d4-0000-0000-0000-000000000001?isFromAd=false\n```\n            \n**Example response:**\n```json\n{\n  \"accountId\": \"a1b2c3d4-0000-0000-0000-000000000000\",\n  \"billboardId\": \"a1b2c3d4-0000-0000-0000-000000000001\",\n  \"storefrontId\": \"a1b2c3d4-0000-0000-0000-000000000002\",\n  \"title\": \"50% off all pizzas tonight\",\n  \"description\": \"Dine-in only. Valid until 10pm.\",\n  \"created\": \"2024-01-01T00:00:00Z\",\n  \"updated\": \"2024-01-02T00:00:00Z\",\n  \"configureStart\": false,\n  \"start\": null,\n  \"configureEnd\": false,\n  \"end\": null,\n  \"storefrontName\": \"Mario's Pizza\",\n  \"locationDescription\": \"123 Main St, Springfield\",\n  \"locationLat\": 37.7749,\n  \"locationLng\": -122.4194,\n  \"altitudeMeters\": null,\n  \"categoryId\": \"a1b2c3d4-0000-0000-0000-000000000003\",\n  \"isFavorited\": false,\n  \"isReported\": false,\n  \"storefrontImageId\": \"img_abc123\",\n  \"billboardImageId\": \"img_def456\",\n  \"billboardVideoId\": null,\n  \"isEnabled\": true,\n  \"isFeatured\": false,\n  \"unfeatureAt\": null,\n  \"publicApi\": true,\n  \"mondayEnabled\": true,\n  \"monday24Hours\": false,\n  \"mondayOpen\": \"09:00\",\n  \"mondayClose\": \"22:00\",\n  \"tuesdayEnabled\": true,\n  \"tuesday24Hours\": false,\n  \"tuesdayOpen\": \"09:00\",\n  \"tuesdayClose\": \"22:00\",\n  \"wednesdayEnabled\": true,\n  \"wednesday24Hours\": false,\n  \"wednesdayOpen\": \"09:00\",\n  \"wednesdayClose\": \"22:00\",\n  \"thursdayEnabled\": true,\n  \"thursday24Hours\": false,\n  \"thursdayOpen\": \"09:00\",\n  \"thursdayClose\": \"22:00\",\n  \"fridayEnabled\": true,\n  \"friday24Hours\": false,\n  \"fridayOpen\": \"09:00\",\n  \"fridayClose\": \"23:00\",\n  \"saturdayEnabled\": true,\n  \"saturday24Hours\": false,\n  \"saturdayOpen\": \"10:00\",\n  \"saturdayClose\": \"23:00\",\n  \"sundayEnabled\": false,\n  \"sunday24Hours\": false,\n  \"sundayOpen\": null,\n  \"sundayClose\": null\n}\n```",
        "parameters": [
          {
            "name": "storefrontId",
            "in": "path",
            "description": "The storefront ID of the deal.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "billboardId",
            "in": "path",
            "description": "The billboard ID of the deal.",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "isFromAd",
            "in": "query",
            "description": "Pass `true` if the user navigated here from an ad impression.",
            "style": "form",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Full deal details including storefront hours.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealResponseDetails"
                }
              }
            }
          }
        }
      }
    },
    "/global-config/all": {
      "get": {
        "tags": [
          "GlobalConfig"
        ],
        "summary": "Get global config",
        "description": "Returns platform-wide configuration including advertising costs. `BillboardDayAdCost` is the per-day featured cost stored as cents × 100 (e.g. 500 = $0.05).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/GlobalConfigResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GlobalConfigResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/GlobalConfigResponse"
                }
              }
            }
          }
        }
      }
    },
    "/posts": {
      "post": {
        "tags": [
          "Post"
        ],
        "summary": "Create a post",
        "description": "Creates a new post. Supply either `text`, `imageId`, `videoId`, or any combination.\nSet `isActive` to `true` to publish immediately.\nTo create a comment (sub-post) set `parentPostId` to the parent post's ID.\n            \nTo attach an image, upload it via `POST /blobs` with category `post-image` (recommended width: 640 px, height varies).\nTo attach a video, upload it via `POST /blobs` with category `post-video`.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PostRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PostRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/posts/{id}": {
      "put": {
        "tags": [
          "Post"
        ],
        "summary": "Update a post",
        "description": "Updates an existing post owned by the authenticated account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PostRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PostRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "Post"
        ],
        "summary": "Delete a post",
        "description": "Permanently deletes a post owned by the authenticated account.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "get": {
        "tags": [
          "Post"
        ],
        "summary": "Get a post",
        "description": "Returns a single post by ID. Does not require authentication.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "style": "simple",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AiImageRequest": {
        "required": [
          "prompt"
        ],
        "type": "object",
        "properties": {
          "prompt": {
            "maxLength": 2000,
            "minLength": 1,
            "type": "string"
          },
          "aiProvider": {
            "$ref": "#/components/schemas/AiProviderType"
          },
          "width": {
            "maximum": 8192,
            "minimum": 1,
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "height": {
            "maximum": 8192,
            "minimum": 1,
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "AiProviderResponse": {
        "type": "object",
        "properties": {
          "type": {
            "type": "integer",
            "format": "int32"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "supportsImage": {
            "type": "boolean"
          },
          "supportsText": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "AiProviderType": {
        "enum": [
          "OpenAi",
          "GoogleAi",
          "ClaudeAi",
          "PerplexityAi",
          "Grok"
        ],
        "type": "string"
      },
      "DealResponse": {
        "type": "object",
        "properties": {
          "accountId": {
            "type": "string",
            "format": "uuid"
          },
          "billboardId": {
            "type": "string",
            "format": "uuid"
          },
          "storefrontId": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          },
          "configureStart": {
            "type": "boolean"
          },
          "start": {
            "type": "string",
            "nullable": true
          },
          "configureEnd": {
            "type": "boolean"
          },
          "end": {
            "type": "string",
            "nullable": true
          },
          "storefrontName": {
            "type": "string",
            "nullable": true
          },
          "locationDescription": {
            "type": "string",
            "nullable": true
          },
          "locationLat": {
            "type": "number",
            "format": "float"
          },
          "locationLng": {
            "type": "number",
            "format": "float"
          },
          "altitudeMeters": {
            "type": "number",
            "format": "float",
            "nullable": true
          },
          "categoryId": {
            "type": "string",
            "format": "uuid"
          },
          "isFavorited": {
            "type": "boolean"
          },
          "isReported": {
            "type": "boolean"
          },
          "storefrontImageId": {
            "type": "string",
            "nullable": true
          },
          "billboardImageId": {
            "type": "string",
            "nullable": true
          },
          "billboardVideoId": {
            "type": "string",
            "nullable": true
          },
          "isEnabled": {
            "type": "boolean"
          },
          "isFeatured": {
            "type": "boolean"
          },
          "unfeatureAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "publicApi": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "DealResponseDetails": {
        "type": "object",
        "properties": {
          "accountId": {
            "type": "string",
            "format": "uuid"
          },
          "billboardId": {
            "type": "string",
            "format": "uuid"
          },
          "storefrontId": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          },
          "configureStart": {
            "type": "boolean"
          },
          "start": {
            "type": "string",
            "nullable": true
          },
          "configureEnd": {
            "type": "boolean"
          },
          "end": {
            "type": "string",
            "nullable": true
          },
          "storefrontName": {
            "type": "string",
            "nullable": true
          },
          "locationDescription": {
            "type": "string",
            "nullable": true
          },
          "locationLat": {
            "type": "number",
            "format": "float"
          },
          "locationLng": {
            "type": "number",
            "format": "float"
          },
          "altitudeMeters": {
            "type": "number",
            "format": "float",
            "nullable": true
          },
          "categoryId": {
            "type": "string",
            "format": "uuid"
          },
          "isFavorited": {
            "type": "boolean"
          },
          "isReported": {
            "type": "boolean"
          },
          "storefrontImageId": {
            "type": "string",
            "nullable": true
          },
          "billboardImageId": {
            "type": "string",
            "nullable": true
          },
          "billboardVideoId": {
            "type": "string",
            "nullable": true
          },
          "isEnabled": {
            "type": "boolean"
          },
          "isFeatured": {
            "type": "boolean"
          },
          "unfeatureAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "publicApi": {
            "type": "boolean"
          },
          "mondayEnabled": {
            "type": "boolean"
          },
          "monday24Hours": {
            "type": "boolean"
          },
          "mondayOpen": {
            "type": "string",
            "nullable": true
          },
          "mondayClose": {
            "type": "string",
            "nullable": true
          },
          "tuesdayEnabled": {
            "type": "boolean"
          },
          "tuesday24Hours": {
            "type": "boolean"
          },
          "tuesdayOpen": {
            "type": "string",
            "nullable": true
          },
          "tuesdayClose": {
            "type": "string",
            "nullable": true
          },
          "wednesdayEnabled": {
            "type": "boolean"
          },
          "wednesday24Hours": {
            "type": "boolean"
          },
          "wednesdayOpen": {
            "type": "string",
            "nullable": true
          },
          "wednesdayClose": {
            "type": "string",
            "nullable": true
          },
          "thursdayEnabled": {
            "type": "boolean"
          },
          "thursday24Hours": {
            "type": "boolean"
          },
          "thursdayOpen": {
            "type": "string",
            "nullable": true
          },
          "thursdayClose": {
            "type": "string",
            "nullable": true
          },
          "fridayEnabled": {
            "type": "boolean"
          },
          "friday24Hours": {
            "type": "boolean"
          },
          "fridayOpen": {
            "type": "string",
            "nullable": true
          },
          "fridayClose": {
            "type": "string",
            "nullable": true
          },
          "saturdayEnabled": {
            "type": "boolean"
          },
          "saturday24Hours": {
            "type": "boolean"
          },
          "saturdayOpen": {
            "type": "string",
            "nullable": true
          },
          "saturdayClose": {
            "type": "string",
            "nullable": true
          },
          "sundayEnabled": {
            "type": "boolean"
          },
          "sunday24Hours": {
            "type": "boolean"
          },
          "sundayOpen": {
            "type": "string",
            "nullable": true
          },
          "sundayClose": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "GlobalConfigResponse": {
        "type": "object",
        "properties": {
          "billboardDayAdCost": {
            "type": "integer",
            "format": "int32"
          },
          "billingDayStart": {
            "type": "string",
            "nullable": true
          },
          "billingDayEnd": {
            "type": "string",
            "nullable": true
          },
          "affiliateCommissionPercentage": {
            "type": "integer",
            "format": "int32"
          },
          "minPayout": {
            "type": "integer",
            "format": "int32"
          },
          "maxPayout": {
            "type": "integer",
            "format": "int32"
          },
          "oneMonthDayAdCost": {
            "type": "integer",
            "format": "int32"
          },
          "threeMonthDayAdCost": {
            "type": "integer",
            "format": "int32"
          },
          "sixMonthDayAdCost": {
            "type": "integer",
            "format": "int32"
          },
          "twelveMonthDayAdCost": {
            "type": "integer",
            "format": "int32"
          },
          "aiProviders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AiProviderResponse"
            },
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "PostRequest": {
        "required": [
          "isActive"
        ],
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "nullable": true
          },
          "imageId": {
            "type": "string",
            "nullable": true
          },
          "videoId": {
            "type": "string",
            "nullable": true
          },
          "videoUrl": {
            "type": "string",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          },
          "parentPostId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "isMature": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "QuickUpdateDealRequest": {
        "required": [
          "description",
          "fridayEnabled",
          "locationDescription",
          "locationLat",
          "locationLng",
          "mondayEnabled",
          "name",
          "saturdayEnabled",
          "sundayEnabled",
          "thursdayEnabled",
          "title",
          "tuesdayEnabled",
          "wednesdayEnabled"
        ],
        "type": "object",
        "properties": {
          "title": {
            "maxLength": 18,
            "minLength": 1,
            "type": "string"
          },
          "description": {
            "maxLength": 1000,
            "minLength": 1,
            "type": "string"
          },
          "imageId": {
            "type": "string",
            "nullable": true
          },
          "videoId": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "minLength": 1,
            "type": "string"
          },
          "locationId": {
            "type": "string",
            "nullable": true
          },
          "locationDescription": {
            "minLength": 1,
            "type": "string"
          },
          "locationLat": {
            "type": "number",
            "format": "float"
          },
          "locationLng": {
            "type": "number",
            "format": "float"
          },
          "altitudeMeters": {
            "maximum": 1000,
            "minimum": 0,
            "type": "number",
            "format": "float",
            "nullable": true
          },
          "categoryId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "mondayEnabled": {
            "type": "boolean"
          },
          "monday24Hours": {
            "type": "boolean"
          },
          "mondayOpen": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "mondayClose": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "tuesdayEnabled": {
            "type": "boolean"
          },
          "tuesday24Hours": {
            "type": "boolean"
          },
          "tuesdayOpen": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "tuesdayClose": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "wednesdayEnabled": {
            "type": "boolean"
          },
          "wednesday24Hours": {
            "type": "boolean"
          },
          "wednesdayOpen": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "wednesdayClose": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "thursdayEnabled": {
            "type": "boolean"
          },
          "thursday24Hours": {
            "type": "boolean"
          },
          "thursdayOpen": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "thursdayClose": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "fridayEnabled": {
            "type": "boolean"
          },
          "friday24Hours": {
            "type": "boolean"
          },
          "fridayOpen": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "fridayClose": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "saturdayEnabled": {
            "type": "boolean"
          },
          "saturday24Hours": {
            "type": "boolean"
          },
          "saturdayOpen": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "saturdayClose": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "sundayEnabled": {
            "type": "boolean"
          },
          "sunday24Hours": {
            "type": "boolean"
          },
          "sundayOpen": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "sundayClose": {
            "pattern": "^\\d{2}:\\d{2}$",
            "type": "string",
            "nullable": true
          },
          "isEnabled": {
            "type": "boolean"
          },
          "isFeatured": {
            "type": "boolean"
          },
          "unfeatureAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "StorefrontCategoryResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "isActive": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "name": "X-Api-Key",
        "in": "header"
      }
    }
  },
  "security": [
    {
      "ApiKey": [ ]
    }
  ]
}