{
  "openapi": "3.1.0",
  "info": {
    "title": "TRAFFIX Cabinet API",
    "version": "1.0.0",
    "description": "JWT-authenticated cabinet API for the official Telegram Ads reseller TRAFFIX (traffixads.com).\n\n## Versioning\nStable paths are under `/api/v1/*` (also available without the version prefix).\nClients may send `API-Version: 1`. Deprecated versions will be announced via `Deprecation` / `Sunset` headers.\n\n## Rate limits\nResponses include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (and legacy `X-RateLimit-*`).\nOn 429, respect `Retry-After`.\n\n## Idempotency\nMutating requests accept `Idempotency-Key`. Successful responses echo the key.\n\n## Errors\nAll errors return JSON `{ success: false, error: { code, message, requestId? } }`.\n\nAuth: https://traffixads.com/auth.md · Developers: https://traffixads.com/developers",
    "contact": {
      "name": "TRAFFIX Support",
      "url": "https://t.me/traffix_support",
      "email": "support@traffixads.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://traffixads.com",
      "description": "Production (path-versioned)"
    },
    {
      "url": "https://traffixads.com/api/v1",
      "description": "Production API v1 base (paths below omit /api/v1)"
    }
  ],
  "tags": [
    {
      "name": "Health"
    },
    {
      "name": "Auth"
    },
    {
      "name": "Campaigns"
    },
    {
      "name": "Agent"
    },
    {
      "name": "Jobs"
    }
  ],
  "paths": {
    "/api/v1/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "Health"
        ],
        "summary": "Service health check",
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Service is healthy",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          },
          "503": {
            "description": "Database unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "operationId": "login",
        "tags": [
          "Auth"
        ],
        "summary": "Obtain JWT access token",
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key for safe retries of POST/PUT/PATCH. Same key within 24h returns the original response.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Authenticated",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "Idempotency-Key": {
                "description": "Echo of the request Idempotency-Key when provided",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/register": {
      "post": {
        "operationId": "register",
        "tags": [
          "Auth"
        ],
        "summary": "Register a new cabinet account",
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key for safe retries of POST/PUT/PATCH. Same key within 24h returns the original response.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Registered",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "operationId": "getCurrentUser",
        "tags": [
          "Auth"
        ],
        "summary": "Current user profile",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Profile",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/campaigns": {
      "get": {
        "operationId": "listCampaigns",
        "tags": [
          "Campaigns"
        ],
        "summary": "List campaigns (paginated)",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "1-based page index",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size (max 100)",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque cursor for keyset pagination (alternative to page)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated campaign list",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignListResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createCampaign",
        "tags": [
          "Campaigns"
        ],
        "summary": "Create a campaign",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key for safe retries of POST/PUT/PATCH. Same key within 24h returns the original response.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCampaignRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/agent/search-guides": {
      "get": {
        "operationId": "searchGuides",
        "tags": [
          "Agent"
        ],
        "summary": "Search TRAFFIX guide articles (markdown results)",
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "description": "Search query, e.g. crypto telegram ads"
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown search results",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuideSearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing query",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/agent/benchmarks": {
      "get": {
        "operationId": "getBenchmarks",
        "tags": [
          "Agent"
        ],
        "summary": "Telegram Ads benchmarks 2026 dataset (markdown)",
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown benchmarks",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/jobs": {
      "post": {
        "operationId": "createJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Start an async agent job (202 Accepted)",
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Client-generated unique key for safe retries of POST/PUT/PATCH. Same key within 24h returns the original response.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateJobRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job accepted",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "Location": {
                "description": "URL of the job resource",
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/jobs/{jobId}": {
      "get": {
        "operationId": "getJob",
        "tags": [
          "Jobs"
        ],
        "summary": "Poll async job status",
        "parameters": [
          {
            "name": "API-Version",
            "in": "header",
            "required": false,
            "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ],
              "default": "1"
            }
          },
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job status",
            "headers": {
              "RateLimit-Limit": {
                "description": "Request quota for the current window (RFC 9338 draft style)",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in the current window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the quota resets",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "description": "Job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Client-generated unique key for safe retries of POST/PUT/PATCH. Same key within 24h returns the original response.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128
        }
      },
      "ApiVersion": {
        "name": "API-Version",
        "in": "header",
        "required": false,
        "description": "Optional API version. Prefer URL path /api/v1/. Supported: 1",
        "schema": {
          "type": "string",
          "enum": [
            "1"
          ],
          "default": "1"
        }
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad_request",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "conflict",
                  "rate_limited",
                  "validation_error",
                  "internal_error",
                  "service_unavailable"
                ]
              },
              "message": {
                "type": "string"
              },
              "requestId": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "success",
          "status"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "error"
            ]
          }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": [
          "emailOrTelegram",
          "password"
        ],
        "properties": {
          "emailOrTelegram": {
            "type": "string",
            "description": "Email or Telegram @username"
          },
          "password": {
            "type": "string",
            "minLength": 6
          },
          "attribution": {
            "type": "string",
            "description": "Optional signup attribution JSON"
          }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "required": [
          "password"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "telegram": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "password": {
            "type": "string",
            "minLength": 6
          }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "telegram": {
            "type": "string",
            "nullable": true
          },
          "role": {
            "type": "string"
          },
          "balance": {
            "type": "number"
          }
        }
      },
      "LoginResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "required": [
              "token",
              "user"
            ],
            "properties": {
              "token": {
                "type": "string"
              },
              "user": {
                "$ref": "#/components/schemas/User"
              }
            }
          }
        }
      },
      "MeResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "$ref": "#/components/schemas/User"
          }
        }
      },
      "Campaign": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "budget": {
            "type": "number"
          },
          "spent": {
            "type": "number"
          },
          "url": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateCampaignRequest": {
        "type": "object",
        "required": [
          "name",
          "budget"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "budget": {
            "type": "number",
            "minimum": 0
          },
          "dailyBudget": {
            "type": "number",
            "minimum": 0
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "CampaignResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "$ref": "#/components/schemas/Campaign"
          }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "required": [
          "page",
          "limit",
          "total",
          "hasMore"
        ],
        "properties": {
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "hasMore": {
            "type": "boolean"
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "CampaignListResponse": {
        "type": "object",
        "required": [
          "success",
          "data",
          "pagination"
        ],
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Campaign"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/PaginationMeta"
          }
        }
      },
      "GuideSearchResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "query": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "slug": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          }
        }
      },
      "CreateJobRequest": {
        "type": "object",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "search-guides",
              "benchmarks"
            ],
            "description": "Async job type"
          },
          "query": {
            "type": "string",
            "description": "Required when type=search-guides"
          }
        }
      },
      "Job": {
        "type": "object",
        "required": [
          "id",
          "type",
          "status",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "failed"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "completedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "resultUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          }
        }
      }
    }
  }
}