{
  "openapi": "3.1.0",
  "info": {
    "title": "AviSeek API",
    "version": "1.0.0",
    "description": "Search API for the AviSeek VRChat avatar index.\n\n**Public catalog and world API.** Anyone may build a VRChat world using the public API without account linking or a key. Commercial resale of the API requires written permission. See the [Terms of Service](/terms).\n\nIndex data is aggregated by [VRC Nexus](https://vrc-nexus.online/).\n\n### Rate limits\nEvery endpoint is rate limited per IP address. Responses carry `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`; a `429` also carries `Retry-After`. Read those headers rather than guessing - the limits below are the current values, not a promise.\n\n| Group | Limit |\n| --- | --- |\n| Avatar search and read endpoints | 600 / minute |\n| Bulk catalog (`/avatars/all`) | 30 / minute |\n| Submissions | 100 / minute |\n| Reports | 5 / minute |\n| Signed-in writes | 30 / minute |\n| World endpoints | 240 / minute |\n| Global backstop (default) | 1200 / minute |",
    "contact": {
      "name": "VRChat Legends",
      "url": "https://vrchatlegends.com"
    },
    "license": {
      "name": "Personal, non-commercial use only",
      "url": "https://aviseek.vrchatlegends.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://aviseek.vrchatlegends.com/api",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Catalog",
      "description": "Search the index. No authentication needed."
    },
    {
      "name": "Insights",
      "description": "Public analytics for every listed avatar. Anonymous aggregates only, no authentication needed."
    },
    {
      "name": "Account",
      "description": "Requires a VRChat Legends session cookie. Sign in at /login."
    },
    {
      "name": "Creator",
      "description": "For creators with a VRChat account verified on their VRChat Legends profile."
    },
    {
      "name": "Worlds",
      "description": "Plain GET, plain JSON, no key. Built for VRChat worlds (Udon `VRCStringDownloader` + a JSON parser) but usable by any client. Lists are long: `limit` up to 500, `/world/all` pages 1000 at a time."
    }
  ],
  "components": {
    "schemas": {
      "Avatar": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "avtr_00000000-1111-2222-3333-444444444444"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "authorId": {
            "type": "string"
          },
          "authorName": {
            "type": "string"
          },
          "authorVrclUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Public VRChat Legends profile, when the creator has a verified one."
          },
          "imageUrl": {
            "type": "string",
            "description": "VRChat CDN thumbnail. Empty when we have no image we are allowed to serve."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags as published by VRChat."
          },
          "aiTags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags AviSeek derived from the name and description, drawn from a fixed vocabulary."
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "pc",
                "quest",
                "ios"
              ]
            }
          },
          "performance": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "enum": [
                "Excellent",
                "Good",
                "Medium",
                "Poor",
                "Very Poor"
              ]
            },
            "description": "Known per-platform ratings keyed by pc, quest or ios. Missing means unknown."
          },
          "addedAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "views": {
            "type": "integer"
          },
          "likes": {
            "type": "integer"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "WorldList": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "now": {
            "type": "string",
            "format": "date-time"
          },
          "total": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "nextOffset": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Pass as `offset` for the next page; null when done."
          },
          "avatars": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Avatar"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "aviseek_session",
        "description": "Set by signing in with VRChat Legends."
      }
    }
  },
  "paths": {
    "/discord/status": {
      "get": {
        "tags": [
          "Discord"
        ],
        "summary": "Your Discord connection and Linked Role checks",
        "description": "Requires an AviSeek session. Returns only safe identity, sync status and the five server-derived checks; never OAuth tokens.",
        "responses": {
          "200": {
            "description": "Connection status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Sign in required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/discord/connect": {
      "post": {
        "tags": [
          "Discord"
        ],
        "summary": "Start Discord OAuth",
        "description": "Requires an AviSeek session and same-origin Origin header. Body: {returnTo: \"dashboard\"} or {returnTo: \"linked-roles\"}. Sets a short-lived HttpOnly state cookie and returns a Discord authorization URL.",
        "responses": {
          "200": {
            "description": "Authorization URL.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/discord/callback": {
      "get": {
        "tags": [
          "Discord"
        ],
        "summary": "Discord OAuth callback",
        "description": "Used by Discord after consent. Requires the same AviSeek session and state cookie as the connection request. Register this exact website URL in the Discord Developer Portal.",
        "responses": {
          "303": {
            "description": "Redirects to the Discord dashboard or Linked Roles page."
          }
        }
      }
    },
    "/discord/guilds": {
      "get": {
        "tags": [
          "Discord"
        ],
        "summary": "Discord servers you can administer",
        "description": "Requires a connected Discord account. Lists only servers the user owns or has Administrator permission in. Manage Server alone does not qualify.",
        "responses": {
          "200": {
            "description": "Eligible servers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/discord/guilds/{id}": {
      "get": {
        "tags": [
          "Discord"
        ],
        "summary": "Server bot settings and permitted channels",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Rechecks current Discord Administrator permission and bot membership. Returns an invite when the bot is absent.",
        "responses": {
          "200": {
            "description": "Server configuration.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "403": {
            "description": "Administrator permission required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Discord"
        ],
        "summary": "Update bot settings for your server",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Same-origin authenticated request. Rechecks Administrator permission and effective bot channel permissions. Body requires enabled (boolean), private (boolean), channel (snowflake string or null). Changes apply only to this server.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled",
                  "private",
                  "channel"
                ],
                "additionalProperties": false,
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "private": {
                    "type": "boolean"
                  },
                  "channel": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "403": {
            "description": "Administrator permission required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/discord/sync": {
      "post": {
        "tags": [
          "Discord"
        ],
        "summary": "Sync your Linked Role checks",
        "description": "Same-origin authenticated request. Values come from AviSeek, not the request body. Identity and Patreon claims expire after 24 hours and are cleared on the next successful sync. Discord evaluates eligibility; members join Linked Roles in Discord.",
        "responses": {
          "200": {
            "description": "Updated connection.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/discord/connection": {
      "delete": {
        "tags": [
          "Discord"
        ],
        "summary": "Disconnect Discord",
        "description": "Same-origin authenticated request. Clears role eligibility before revoking and deleting the encrypted OAuth grant. On a Discord outage, retry or remove the connection in Discord.",
        "responses": {
          "200": {
            "description": "Disconnected.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/avatars/search": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Search the index",
        "description": "Tokenized match over name, author, description, VRChat tags and AviSeek tags. A term we hold little for triggers one cached lookup against VRC Nexus.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "author",
            "in": "query",
            "description": "Filter by creator name or id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "description": "Filter by a VRChat tag or an AviSeek tag.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "pc, quest, or ios.",
            "schema": {
              "type": "string",
              "enum": [
                "pc",
                "quest",
                "ios"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Result order.",
            "schema": {
              "type": "string",
              "enum": [
                "recent",
                "oldest",
                "name",
                "popular"
              ],
              "default": "recent"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "1-60.",
            "schema": {
              "type": "integer",
              "default": 24,
              "maximum": 60
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Result offset.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching avatars.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "hasMore": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/avatars/tags": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Most common tags",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-60.",
            "schema": {
              "type": "integer",
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tag list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/avatars/trending": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Most viewed avatars",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-20.",
            "schema": {
              "type": "integer",
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Trending avatars.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/avatars/popular-searches": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Top five search terms",
        "responses": {
          "200": {
            "description": "Search terms.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "terms": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/bot/interactions": {
      "post": {
        "tags": [
          "Discord"
        ],
        "summary": "Receive signed Discord interactions",
        "description": "Discord-only webhook. Configure https://aviseek.vrchatlegends.com/api/bot/interactions in the Developer Portal. Requires X-Signature-Ed25519 and X-Signature-Timestamp with the exact signed JSON body. Opening this URL with GET shows endpoint notes; it does not validate the Discord integration.",
        "responses": {
          "200": {
            "description": "Interaction acknowledgement.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Invalid signature.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Discord public key is not configured.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/avatars/submit": {
      "post": {
        "tags": [
          "Catalog"
        ],
        "summary": "Queue an avatar for indexing",
        "description": "Public endpoint, no login or API key required. Send one avatarId or an avatarIds array of 1 to 50 IDs or avatar URLs. At most 100 submitted items per minute per IP (100 requests for single submissions), including invalid and duplicate items. A batch returns HTTP 200 with accepted/rejected counts and ordered per-item results; check each result status and code. Single duplicates return 409. Queue capacity is 1000; batch overflow items receive queue_full, while a single submission gets HTTP 503. Retry-After advises when to retry. Accepted items use the existing paced background worker and shared VRChat safety budget. Private and blocked avatars remain excluded.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "avatarId": {
                    "type": "string",
                    "example": "avtr_00000000-1111-2222-3333-444444444444"
                  },
                  "avatarIds": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "string"
                    },
                    "example": [
                      "avtr_00000000-1111-2222-3333-444444444444",
                      "avtr_11111111-2222-3333-4444-555555555555"
                    ]
                  }
                },
                "oneOf": [
                  {
                    "required": [
                      "avatarId"
                    ],
                    "not": {
                      "required": [
                        "avatarIds"
                      ]
                    }
                  },
                  {
                    "required": [
                      "avatarIds"
                    ],
                    "not": {
                      "required": [
                        "avatarId"
                      ]
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Single submission queued, or batch processed with ordered results containing index, id, status, position or rejection code.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string"
                    },
                    "id": {
                      "type": "string"
                    },
                    "position": {
                      "type": "integer"
                    },
                    "accepted": {
                      "type": "integer"
                    },
                    "rejected": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not a valid avatar id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Avatar is blocked from submission.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Avatar is already indexed or queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "100 submitted items per minute exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "503": {
            "description": "Verification queue full. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/stats": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Index totals",
        "responses": {
          "200": {
            "description": "Catalog counters.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "indexed": {
                      "type": "integer"
                    },
                    "active": {
                      "type": "integer"
                    },
                    "listed": {
                      "type": "integer"
                    },
                    "hidden": {
                      "type": "integer"
                    },
                    "creators": {
                      "type": "integer"
                    },
                    "queue": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Liveness",
        "responses": {
          "200": {
            "description": "Service is up.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/account/summary": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Identity, settings and usage",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account summary.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Not signed in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/account/settings": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Read AviSeek preferences",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "Settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Account"
        ],
        "summary": "Update AviSeek preferences",
        "description": "Partial update. Unknown keys are ignored and every value is clamped to a valid option.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "defaultPlatform": {
                    "type": "string"
                  },
                  "defaultSort": {
                    "type": "string"
                  },
                  "resultsPerPage": {
                    "type": "integer"
                  },
                  "compactGrid": {
                    "type": "boolean"
                  },
                  "reduceMotion": {
                    "type": "boolean"
                  },
                  "openLinksInNewTab": {
                    "type": "boolean"
                  },
                  "showAiTags": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/favorites": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Saved avatars",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "Collection.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Account"
        ],
        "summary": "Clear the collection",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "Cleared.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/favorites/{id}": {
      "put": {
        "tags": [
          "Account"
        ],
        "summary": "Save an avatar",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Account"
        ],
        "summary": "Unsave an avatar",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/favorites/groups": {
      "post": {
        "tags": [
          "Account"
        ],
        "summary": "Create a favorite group",
        "description": "A group is presentation only: the collection stays one list and each avatar belongs to at most one group, so renaming or deleting a group never unsaves anything.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "icon": {
                    "type": "string",
                    "description": "One of the fixed icon names returned by GET /favorites."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad name, duplicate, or group limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/favorites/groups/{groupId}": {
      "patch": {
        "tags": [
          "Account"
        ],
        "summary": "Rename a group or change its icon",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "groupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "icon": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Unknown group.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Account"
        ],
        "summary": "Delete a group",
        "description": "Its avatars stay in the collection, just without a group.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "groupId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Unknown group.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/favorites/{id}/group": {
      "put": {
        "tags": [
          "Account"
        ],
        "summary": "Move a saved avatar into a group",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "groupId": {
                    "type": "string",
                    "nullable": true,
                    "description": "null removes it from its group."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Moved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Unknown group, or the avatar is not saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/likes/{id}": {
      "put": {
        "tags": [
          "Account"
        ],
        "summary": "Like an avatar",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Liked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Account"
        ],
        "summary": "Unlike an avatar",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Unliked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/creator/avatars": {
      "get": {
        "tags": [
          "Creator"
        ],
        "summary": "Your avatars with analytics",
        "description": "Ownership comes from the VRChat account verified on your VRChat Legends profile. Returns 409 when no verified link exists.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "description": "Length of the daily series, 7-90.",
            "schema": {
              "type": "integer",
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatars and analytics.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "409": {
            "description": "No verified VRChat link.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/creator/avatars/{id}/visibility": {
      "put": {
        "tags": [
          "Creator"
        ],
        "summary": "Unlist or relist your own avatar",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "listed": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Visibility updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/insights/overview": {
      "get": {
        "tags": [
          "Insights"
        ],
        "summary": "Site-wide analytics",
        "description": "Totals for the window (impressions, views, outbound clicks, wears, likes, favorites), the same for the window before it, percent trend per counter, a daily series, and short leaderboards (views, trending, click-through, likes). Cached 30 seconds.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "description": "Window length, 7-90.",
            "schema": {
              "type": "integer",
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Overview.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/insights/avatars": {
      "get": {
        "tags": [
          "Insights"
        ],
        "summary": "Every listed avatar, ranked by a metric",
        "description": "Avatars with recorded activity come first in the chosen order; the quiet rest of the catalog follows newest first, so a text search still finds an avatar with zero views. Each row carries lifetime totals, window totals, the previous window, percent trend, rates (ctr, click-through, like rate, save rate) and a 14 day view sparkline.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Match against name, creator, id and tags.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "author",
            "in": "query",
            "description": "Creator name or id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "pc, quest, or ios.",
            "schema": {
              "type": "string",
              "enum": [
                "pc",
                "quest",
                "ios"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Ranking.",
            "schema": {
              "type": "string",
              "enum": [
                "views",
                "trending",
                "impressions",
                "outbound",
                "ctr",
                "likes",
                "favorites",
                "newest"
              ],
              "default": "views"
            }
          },
          {
            "name": "days",
            "in": "query",
            "description": "Window length, 7-90.",
            "schema": {
              "type": "integer",
              "default": 30
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "1-100.",
            "schema": {
              "type": "integer",
              "default": 30,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Result offset.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "tracked": {
                      "type": "integer",
                      "description": "How many matches have any recorded activity."
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/insights/avatars/{id}": {
      "get": {
        "tags": [
          "Insights"
        ],
        "summary": "Deep dive on one avatar",
        "description": "The explorer row plus a daily series (every counter), audience breakdowns for views and outbound clicks (referring site, device, country, hour, site surface, platform, search terms), rank and percentile against every avatar with activity, peak days, activity span with daily averages, and how many listed avatars the creator has.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "description": "Window length, 7-90.",
            "schema": {
              "type": "integer",
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar insights.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Not listed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/avatars/all": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Every listed avatar, paginated",
        "description": "For developers mirroring the catalog. Pages of up to 1000; follow `nextOffset` until it is null. Cached two minutes.",
        "parameters": [
          {
            "name": "offset",
            "in": "query",
            "description": "Result offset.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "1-1000.",
            "schema": {
              "type": "integer",
              "default": 1000,
              "maximum": 1000
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Order.",
            "schema": {
              "type": "string",
              "enum": [
                "recent",
                "popular",
                "name"
              ],
              "default": "recent"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/avatars/events": {
      "get": {
        "tags": [
          "Catalog"
        ],
        "summary": "Live avatar events (server-sent events)",
        "description": "A `text/event-stream`. Moderation frames are `{\"type\":\"blocked\"|\"unblocked\",\"id\":\"avtr_...\",\"at\":\"...\"}`. Counter frames are `{\"type\":\"counts\",\"id\":\"avtr_...\",\"views\":123,\"likes\":45,\"at\":\"...\"}`. Use the exact counter values rather than incrementing locally.",
        "responses": {
          "200": {
            "description": "Event stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/avatars/{id}/track": {
      "post": {
        "tags": [
          "Catalog"
        ],
        "summary": "Count a view or outbound click",
        "description": "Anonymous. The referring site, device family, CDN country and search term are bucketed for the insights charts; no identifiers are stored.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "view",
                      "outbound"
                    ]
                  },
                  "source": {
                    "type": "string",
                    "description": "Where on the site the click happened, e.g. search, trending, favorites."
                  },
                  "platform": {
                    "type": "string"
                  },
                  "term": {
                    "type": "string",
                    "description": "The search text that surfaced the avatar, if any."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Counted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/ping": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Liveness plus limits",
        "responses": {
          "200": {
            "description": "Alive.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "string"
                    },
                    "maxLimit": {
                      "type": "integer"
                    },
                    "allPage": {
                      "type": "integer"
                    },
                    "schema": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/endpoints": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Every world route with its parameters",
        "responses": {
          "200": {
            "description": "Endpoint table.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/world/stats": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Catalog counts",
        "responses": {
          "200": {
            "description": "Counters.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/world/client": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Public compact world data, up to 120 avatars in one download",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search text",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "author",
            "in": "query",
            "description": "Creator name or id",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Result offset",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Public catalog, supporters, stats. No account data.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/world/search": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Search the catalog",
        "description": "Same index as `/avatars/search`, with long pages (up to 500) and a `nextOffset` cursor. Every avatar carries `id`, `name`, `authorName`, `authorId`, `imageUrl`, `tags`, `aiTags`, `platforms`, `views` and `likes`.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search text.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "author",
            "in": "query",
            "description": "Creator name or id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "description": "VRChat or AviSeek tag.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "pc, quest, or ios.",
            "schema": {
              "type": "string",
              "enum": [
                "pc",
                "quest",
                "ios"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Order.",
            "schema": {
              "type": "string",
              "enum": [
                "popular",
                "recent",
                "oldest",
                "name"
              ],
              "default": "popular"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "1-500.",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 500
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Result offset.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Pass as `offset` for the next page; null when done."
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/list": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Browse without a query",
        "parameters": [
          {
            "name": "sort",
            "in": "query",
            "description": "Order.",
            "schema": {
              "type": "string",
              "enum": [
                "popular",
                "recent",
                "oldest",
                "name"
              ],
              "default": "popular"
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "pc, quest, or ios.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "1-500.",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 500
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Result offset.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Pass as `offset` for the next page; null when done."
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/all": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Every visible avatar, 1000 per page",
        "parameters": [
          {
            "name": "offset",
            "in": "query",
            "description": "Result offset.",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "1-1000.",
            "schema": {
              "type": "integer",
              "default": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Pass as `offset` for the next page; null when done."
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/trending": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Rising this week",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-500.",
            "schema": {
              "type": "integer",
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Pass as `offset` for the next page; null when done."
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/promoted": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Paid avatar placements",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-50.",
            "schema": {
              "type": "integer",
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Pass as `offset` for the next page; null when done."
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/promoted/groups": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Paid VRChat group placements",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-50.",
            "schema": {
              "type": "integer",
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Group list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/random": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "A different handful each call",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-500.",
            "schema": {
              "type": "integer",
              "default": 24
            }
          },
          {
            "name": "platform",
            "in": "query",
            "description": "pc, quest, or ios.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Pass as `offset` for the next page; null when done."
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/avatars": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Several avatars by id",
        "parameters": [
          {
            "name": "ids",
            "in": "query",
            "description": "Comma separated avtr_ ids, up to 500.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar list plus `missing` ids.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "count": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "nextOffset": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Pass as `offset` for the next page; null when done."
                    },
                    "avatars": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/avatar/{id}": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "One avatar (counts a view)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Avatar.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "avatar": {
                      "$ref": "#/components/schemas/Avatar"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown avatar.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/world/tags": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Most used tags",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-200.",
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tags with counts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/world/searches": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Popular search terms",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-50.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Terms.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/world/leaderboard": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Richest accounts",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-50.",
            "schema": {
              "type": "integer",
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rank, username, balance, avatar.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/world/supporters": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Supporters board",
        "description": "Active Patreon members of VRChat Legends with their tier title. Cached ten minutes.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "1-200.",
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Name and tier per supporter.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/world/admins": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Display names the world may treat as staff",
        "responses": {
          "200": {
            "description": "Admin list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/world/bundle": {
      "get": {
        "tags": [
          "Worlds"
        ],
        "summary": "Several sections in one call",
        "parameters": [
          {
            "name": "include",
            "in": "query",
            "description": "Comma separated: stats, trending, promoted, promotedGroups, random, tags, searches, leaderboard, admins, supporters.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Rows per list section.",
            "schema": {
              "type": "integer",
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{ sections: { name: payload } }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "No valid section named.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
