{
  "openapi": "3.0.3",
  "info": {
    "title": "VerifyUGC Public API",
    "version": "1.0.0",
    "description": "Read-only access to the VerifyUGC global blacklist and public creator trust data. Most endpoints require an API key on the $5/month developer plan; the `/v1/public/*`, `/v1/stats/public`, `/referrals/leaderboard` and `/badge/*` endpoints are free and need no key.\n\nThis document is **generated** from the live route handlers by `npm run generate:openapi` (see `scripts/generate-openapi.mjs`). Do not edit it by hand — edit the `@openapi` annotation blocks above each route handler in `src/routes/*.ts` and re-run the generator.",
    "x-generated-by": "scripts/generate-openapi.mjs",
    "x-generated-note": "DO NOT EDIT BY HAND. Edit the @openapi blocks in src/routes/*.ts and run `npm run generate:openapi`."
  },
  "servers": [
    {
      "url": "https://verifyugc.dev"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Blacklist",
      "description": "Query the global scammer/ban blacklist (API key required)."
    },
    {
      "name": "Creators",
      "description": "Public creator profiles and Trust Scores (API key required)."
    },
    {
      "name": "Public",
      "description": "Free, keyless endpoints powering the on-site tools and embeds."
    },
    {
      "name": "Account",
      "description": "Endpoints scoped to the calling API key."
    },
    {
      "name": "Courses",
      "description": "Creator-education courses. List/detail are open; submitting answers requires a logged-in session."
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your vugc_live_... API key."
      },
      "cookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "session",
        "description": "A logged-in VerifyUGC web session cookie. Used by the on-site app, not by API-key integrations."
      }
    },
    "schemas": {
      "BlacklistHit": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "banned": {
            "type": "boolean"
          },
          "entry": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string"
              },
              "severity": {
                "type": "string",
                "enum": [
                  "warn",
                  "restrict",
                  "ban"
                ]
              },
              "scope": {
                "type": "string",
                "enum": [
                  "global",
                  "vertical"
                ]
              },
              "vertical": {
                "type": "string"
              },
              "reason_code": {
                "type": "string"
              },
              "summary": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "checked_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BatchCheckResult": {
        "type": "object",
        "description": "One row per submitted account, returned in input order.",
        "properties": {
          "provider": {
            "type": "string",
            "nullable": true
          },
          "id": {
            "type": "string",
            "nullable": true
          },
          "banned": {
            "type": "boolean"
          },
          "high_risk": {
            "type": "boolean",
            "description": "Present and true when the account is flagged high-risk."
          },
          "watchlist": {
            "type": "boolean",
            "description": "Present and true when the account is on a watchlist."
          },
          "severity": {
            "type": "string",
            "enum": [
              "warn",
              "restrict",
              "ban"
            ],
            "description": "Present only on a hit."
          },
          "scope": {
            "type": "string",
            "enum": [
              "global",
              "vertical"
            ],
            "description": "Present only on a hit."
          },
          "reason_code": {
            "type": "string",
            "description": "Present only on a hit."
          },
          "error": {
            "type": "string",
            "description": "Present when the row could not be checked (e.g. `invalid_account`)."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "BlacklistEntry": {
        "type": "object",
        "description": "Public-safe view of an active blacklist entry.",
        "properties": {
          "id": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "warn",
              "restrict",
              "ban"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "global",
              "vertical"
            ]
          },
          "vertical": {
            "type": "string",
            "enum": [
              "roblox",
              "uefn",
              "minecraft",
              "all"
            ]
          },
          "reason_code": {
            "type": "string"
          },
          "summary": {
            "type": "string",
            "nullable": true
          },
          "issued_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "appealable": {
            "type": "boolean"
          }
        }
      },
      "BlacklistEntryPage": {
        "type": "object",
        "description": "A cursor-paginated page of blacklist entries.",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BlacklistEntry"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true,
            "description": "Opaque cursor for the next page (pass back as `?cursor=`). Null on the last page."
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "CreatorProfile": {
        "type": "object",
        "description": "Public creator profile with headline Trust Score.",
        "properties": {
          "handle": {
            "type": "string"
          },
          "display_name": {
            "type": "string",
            "nullable": true
          },
          "verification_level": {
            "type": "integer"
          },
          "verification_label": {
            "type": "string"
          },
          "karma": {
            "type": "integer"
          },
          "trust_score": {
            "type": "integer",
            "description": "Composite Trust Score, 0–250."
          },
          "trust_band": {
            "type": "string"
          },
          "reviews": {
            "type": "object",
            "properties": {
              "count": {
                "type": "integer"
              },
              "average": {
                "type": "number",
                "nullable": true
              }
            }
          },
          "completed_deals": {
            "type": "integer"
          },
          "linked_accounts": {
            "type": "integer"
          },
          "blacklisted": {
            "type": "boolean"
          },
          "joined_at": {
            "type": "string",
            "format": "date-time"
          },
          "profile_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "TrustBreakdown": {
        "type": "object",
        "description": "Composite Trust Score (0–250) plus the coarse public basis behind it. Penalty internals are never exposed.",
        "properties": {
          "handle": {
            "type": "string"
          },
          "trust_score": {
            "type": "integer"
          },
          "band": {
            "type": "string"
          },
          "trend": {
            "type": "string",
            "description": "Direction of the score over recent history (e.g. `up`, `down`, `flat`)."
          },
          "blacklisted": {
            "type": "boolean"
          },
          "basis": {
            "type": "object",
            "properties": {
              "verification_level": {
                "type": "integer"
              },
              "verification_label": {
                "type": "string"
              },
              "account_age_days": {
                "type": "integer"
              },
              "linked_accounts": {
                "type": "integer"
              },
              "completed_deals": {
                "type": "integer"
              },
              "reviews": {
                "type": "object",
                "properties": {
                  "count": {
                    "type": "integer"
                  },
                  "average": {
                    "type": "number",
                    "nullable": true
                  }
                }
              }
            }
          },
          "computed_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Connection": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string"
          },
          "provider_username": {
            "type": "string",
            "nullable": true
          },
          "provider_url": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "verified_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "PublicStats": {
        "type": "object",
        "description": "Cached social-proof counters for the marketing site.",
        "properties": {
          "creators_verified": {
            "type": "integer"
          },
          "servers_protected": {
            "type": "integer"
          },
          "entries_listed": {
            "type": "integer"
          },
          "deals_completed": {
            "type": "integer"
          },
          "cached": {
            "type": "boolean",
            "description": "True when served from the KV cache rather than freshly computed."
          }
        }
      },
      "ReferralLeaderboard": {
        "type": "object",
        "description": "Public referral leaderboard. Privacy-safe: handle + count + badge only, never earnings.",
        "properties": {
          "period": {
            "type": "string",
            "enum": [
              "all",
              "month"
            ]
          },
          "leaders": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "rank": {
                  "type": "integer"
                },
                "handle": {
                  "type": "string"
                },
                "count": {
                  "type": "integer"
                },
                "badge": {
                  "type": "object",
                  "nullable": true,
                  "properties": {
                    "tier": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "CourseSummary": {
        "type": "object",
        "description": "A course with the viewer's completion status. Status is `locked` when signed out, else `available` or `passed`.",
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "platform_tag": {
            "type": "string",
            "nullable": true
          },
          "difficulty": {
            "type": "string",
            "nullable": true
          },
          "pass_threshold": {
            "type": "integer",
            "description": "Percentage score required to pass."
          },
          "question_count": {
            "type": "integer"
          },
          "points": {
            "type": "integer",
            "description": "Trust points awarded for passing."
          },
          "status": {
            "type": "string",
            "enum": [
              "locked",
              "available",
              "passed"
            ]
          },
          "score": {
            "type": "integer",
            "nullable": true,
            "description": "The viewer's best score, when signed in and attempted."
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "CourseDetail": {
        "type": "object",
        "description": "Course detail with quiz questions. Option order is shuffled per request; each option carries its original index as `key`. Correct answers and explanations are never included here.",
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "platform_tag": {
            "type": "string",
            "nullable": true
          },
          "difficulty": {
            "type": "string",
            "nullable": true
          },
          "pass_threshold": {
            "type": "integer"
          },
          "points": {
            "type": "integer"
          },
          "question_count": {
            "type": "integer"
          },
          "questions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "position": {
                  "type": "integer"
                },
                "question": {
                  "type": "string"
                },
                "options": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "key": {
                        "type": "integer",
                        "description": "Original option index — submit this as `answer_index`."
                      },
                      "text": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "completion": {
            "type": "object",
            "nullable": true,
            "properties": {
              "passed": {
                "type": "boolean"
              },
              "score": {
                "type": "integer"
              },
              "completed_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "CourseGrade": {
        "type": "object",
        "description": "Result of grading a course submission.",
        "properties": {
          "passed": {
            "type": "boolean"
          },
          "score": {
            "type": "integer",
            "description": "Percentage score."
          },
          "correct_count": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "pass_threshold": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "description": "Per-question outcome with the correct answer and explanation now revealed.",
            "items": {
              "type": "object"
            }
          },
          "trust_buff": {
            "type": "object",
            "properties": {
              "applied": {
                "type": "boolean"
              },
              "per_course": {
                "type": "integer"
              },
              "max_points": {
                "type": "integer"
              },
              "passed_count": {
                "type": "integer"
              },
              "earned_points": {
                "type": "integer"
              }
            }
          }
        }
      },
      "UsageStats": {
        "type": "object",
        "description": "Usage and rate-limit status for the calling API key.",
        "properties": {
          "key_id": {
            "type": "string"
          },
          "rate_limit_tier": {
            "type": "string",
            "enum": [
              "free",
              "verified",
              "partner"
            ]
          },
          "api_tier": {
            "type": "string",
            "enum": [
              "free",
              "growth",
              "enterprise"
            ],
            "description": "The key owner's subscription tier, which drives the rate-limit ceiling."
          },
          "total_requests": {
            "type": "integer",
            "description": "All-time metered requests for this key (retained ~3 months)."
          },
          "requests_this_month": {
            "type": "integer",
            "description": "Metered requests in the current UTC month."
          },
          "rate_limit": {
            "type": "object",
            "properties": {
              "limit": {
                "type": "integer",
                "description": "Requests allowed per window for this key's tier."
              },
              "used": {
                "type": "integer"
              },
              "remaining": {
                "type": "integer"
              },
              "reset": {
                "type": "integer",
                "description": "Unix epoch seconds when the current window resets."
              },
              "window_seconds": {
                "type": "integer"
              }
            }
          },
          "by_endpoint": {
            "type": "array",
            "description": "Per-endpoint request counts, descending.",
            "items": {
              "type": "object",
              "properties": {
                "endpoint": {
                  "type": "string",
                  "description": "Method + route pattern, e.g. `GET /v1/blacklist/check`."
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    },
    "parameters": {
      "cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Opaque pagination cursor from a previous response's `next_cursor`."
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "default": 50,
          "minimum": 1,
          "maximum": 100
        },
        "description": "Page size (1–100, default 50)."
      }
    }
  },
  "paths": {
    "/badge/{handle}": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Embeddable verification badge (SVG)",
        "description": "Server-rendered SVG trust badge for embedding via `<img src=\"https://verifyugc.dev/badge/@handle\">`. Keyless and CDN-cached (1h). Always returns 200 — unknown handles render a neutral \"Not Verified\" badge so a pasted embed never breaks. A trailing `.svg` on the handle is accepted.",
        "security": [],
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Creator handle. A leading @ and/or trailing .svg are stripped."
          },
          {
            "name": "theme",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "light",
                "dark"
              ],
              "default": "dark"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SVG badge",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/referrals/leaderboard": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Public referral leaderboard",
        "description": "Top referrers, ranked. Privacy-safe — handle, referral count and badge only, never earnings. Keyless and per-IP rate-limited.",
        "security": [],
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "month"
              ],
              "default": "all"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReferralLeaderboard"
                }
              }
            }
          }
        }
      }
    },
    "/v1/blacklist": {
      "get": {
        "tags": [
          "Blacklist"
        ],
        "summary": "List blacklist entries (cursor-paginated)",
        "description": "Returns a cursor-paginated list of active, public-safe blacklist entries. Page with `?cursor=&limit=`; the response carries `next_cursor` (pass it back to fetch the next page) and `has_more`. Use `?since=` for incremental sync — poll with the timestamp of your last sync to receive only newer entries.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "name": "vertical",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "roblox",
                "uefn",
                "minecraft",
                "all"
              ]
            },
            "description": "Only entries for this vertical."
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only entries issued strictly after this ISO-8601 timestamp (incremental sync)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlacklistEntryPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid `cursor`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Subscription required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown `vertical`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/blacklist/batch": {
      "post": {
        "tags": [
          "Blacklist"
        ],
        "summary": "Batch check up to 50 ids (Growth tier)",
        "description": "Requires a Growth or Enterprise plan. Accepts `{ items: [{provider, id}] }` (max 50) and returns a `results` array in input order. Distinct from `/check/batch` (which uses the `accounts` key and allows 100 on the base plan).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items"
                ],
                "properties": {
                  "vertical": {
                    "type": "string",
                    "enum": [
                      "roblox",
                      "uefn",
                      "minecraft",
                      "all"
                    ]
                  },
                  "items": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "type": "object",
                      "required": [
                        "provider",
                        "id"
                      ],
                      "properties": {
                        "provider": {
                          "type": "string"
                        },
                        "id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BatchCheckResult"
                      }
                    },
                    "checked_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`items` missing or empty",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Growth or Enterprise plan required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "More than 50 items",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/blacklist/check": {
      "get": {
        "tags": [
          "Blacklist"
        ],
        "summary": "Check a single account",
        "description": "Checks one account against the global blacklist. Both `provider` and `id` are required. To search an id across every platform at once without a key, use the free `GET /v1/public/blacklist/check` instead.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "discord",
                "roblox",
                "epic",
                "minecraft",
                "youtube",
                "twitch",
                "twitter",
                "tiktok",
                "github"
              ]
            },
            "description": "Platform to scope the check to."
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native platform user id to check."
          },
          {
            "name": "vertical",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "roblox",
                "uefn",
                "minecraft",
                "all"
              ],
              "default": "all"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/BlacklistHit"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "provider": {
                          "type": "string"
                        },
                        "id": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing `provider` or `id`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Subscription required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Unknown `vertical`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/blacklist/check/batch": {
      "post": {
        "tags": [
          "Blacklist"
        ],
        "summary": "Batch check up to 100 accounts",
        "description": "Checks many accounts in one call. Send `{ accounts: [{provider, id}] }` (max 100); the response is a `results` array in input order. Available on the base developer plan.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "accounts"
                ],
                "properties": {
                  "vertical": {
                    "type": "string",
                    "enum": [
                      "roblox",
                      "uefn",
                      "minecraft",
                      "all"
                    ]
                  },
                  "accounts": {
                    "type": "array",
                    "maxItems": 100,
                    "items": {
                      "type": "object",
                      "required": [
                        "provider",
                        "id"
                      ],
                      "properties": {
                        "provider": {
                          "type": "string"
                        },
                        "id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BatchCheckResult"
                      }
                    },
                    "checked_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`accounts` missing or empty",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Subscription required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "More than 100 accounts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/blacklist/entries/{id}": {
      "get": {
        "tags": [
          "Blacklist"
        ],
        "summary": "Get a blacklist entry (public-safe fields)",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlacklistEntry"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Subscription required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/courses": {
      "get": {
        "tags": [
          "Courses"
        ],
        "summary": "List creator-education courses",
        "description": "Every course with the viewer's completion status. Works signed-out (all come back `locked`). When a session cookie is present, each course is `available` or `passed` and carries the best score. Also returns the viewer's aggregate Trust-points earned from courses.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "courses": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CourseSummary"
                      }
                    },
                    "trust": {
                      "type": "object",
                      "properties": {
                        "signed_in": {
                          "type": "boolean"
                        },
                        "per_course": {
                          "type": "integer"
                        },
                        "max_points": {
                          "type": "integer"
                        },
                        "passed_count": {
                          "type": "integer"
                        },
                        "earned_points": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/courses/{slug}": {
      "get": {
        "tags": [
          "Courses"
        ],
        "summary": "Course detail with quiz questions",
        "description": "Course detail including quiz questions. Option order is shuffled per request; each option keeps its original index as `key` (submit that as `answer_index`). Correct answers and explanations are never sent here. Open endpoint; includes the viewer's completion when a session cookie is present.",
        "security": [],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseDetail"
                }
              }
            }
          },
          "404": {
            "description": "No such course",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/courses/{slug}/submit": {
      "post": {
        "tags": [
          "Courses"
        ],
        "summary": "Submit course answers for grading",
        "description": "Grades a submission. Requires a logged-in session (cookie auth) — not an API key. `answer_index` is the ORIGINAL option index (the `key` from the detail payload). On a pass, the completion is recorded (best score kept, pass sticky) and the cached Trust Score is recomputed.",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "answers"
                ],
                "properties": {
                  "answers": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "question_id",
                        "answer_index"
                      ],
                      "properties": {
                        "question_id": {
                          "type": "string"
                        },
                        "answer_index": {
                          "type": "integer",
                          "description": "The original option index (the `key` from course detail)."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CourseGrade"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such course",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Course has no questions yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me/usage": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Usage stats for the calling key",
        "description": "Returns the calling API key's total and current-month request counts, its live rate-limit status, and a per-endpoint breakdown. Authenticated by any valid API key — no specific scope required.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageStats"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Subscription required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/public/blacklist/check": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Free, unauthenticated unified blacklist search (rate-limited)",
        "description": "Provider-less unified search: send a single query and it is checked against every supported platform at once. Powers the free public tool widget. No API key required. Per-IP rate-limited (~30/min, 200/day).",
        "security": [],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The query to search — a username, handle, or native id. Matched across all platforms. (`id` is accepted as a legacy alias.)"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": {
                      "type": "string"
                    },
                    "vertical": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BlacklistHit"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing `q`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Daily/burst limit reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/public/resolve": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Resolve a public linked account to its creator",
        "description": "Maps a public linked platform account (provider + native id) to its VerifyUGC creator and Trust Score. Keyless. Privacy-gated: only connections the user marked public are resolvable. Powers inline extension badges.",
        "security": [],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Platform of the linked account, e.g. `roblox`."
          },
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Native platform user id."
          }
        ],
        "responses": {
          "200": {
            "description": "OK — `found` is false when no public connection matches.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "found": {
                      "type": "boolean"
                    },
                    "handle": {
                      "type": "string"
                    },
                    "trust_score": {
                      "type": "integer"
                    },
                    "band": {
                      "type": "string"
                    },
                    "blacklisted": {
                      "type": "boolean"
                    },
                    "profile_url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing `provider` or `id`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/public/trust": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Free public Trust tier lookup",
        "description": "Looks up a creator's public Trust tier (band + label + colour) by handle. Keyless. The raw numeric Trust Score is only included when the caller has a valid VerifyUGC session cookie.",
        "security": [],
        "parameters": [
          {
            "name": "handle",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Creator handle (without the leading @)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "found": {
                      "type": "boolean"
                    },
                    "band": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    },
                    "tier_color": {
                      "type": "string"
                    },
                    "trust_score": {
                      "type": "integer",
                      "description": "Only present for authenticated (session) callers."
                    },
                    "blacklisted": {
                      "type": "boolean"
                    },
                    "checked_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing `handle`",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such creator",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "handle": {
                      "type": "string"
                    },
                    "found": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/v1/stats/public": {
      "get": {
        "tags": [
          "Public"
        ],
        "summary": "Public social-proof counters",
        "description": "Cached homepage counters (creators verified, servers protected, entries listed, deals completed). Keyless. KV-cached ~1h and edge-cacheable.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicStats"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{handle}": {
      "get": {
        "tags": [
          "Creators"
        ],
        "summary": "Public creator profile",
        "description": "Returns a creator's public profile including their headline Trust Score, verification level, review summary and linked-account count.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Creator handle (without the leading @)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatorProfile"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Subscription required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{handle}/connections": {
      "get": {
        "tags": [
          "Creators"
        ],
        "summary": "Public linked accounts for a creator",
        "description": "Lists the creator's public, non-revoked linked platform accounts. Private connections are never returned.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connections": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Connection"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Subscription required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{handle}/trust": {
      "get": {
        "tags": [
          "Creators"
        ],
        "summary": "Creator Trust Score breakdown",
        "description": "The composite Trust Score (0–250) plus the coarse public basis behind it (verification, account age, linked accounts, completed deals, reviews) and a recent trend. Designed to be called before doing business. Penalty internals are never exposed.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrustBreakdown"
                }
              }
            }
          },
          "401": {
            "description": "Missing/invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Subscription required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}
