{
  "openapi": "3.1.0",
  "info": {
    "title": "Dock API",
    "description": "API for managing links between Roblox and Discord accounts, including mapping, updating, removing links, and resetting the custom database.",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.docksys.xyz"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/api/v1/public/roblox-to-discord": {
      "get": {
        "summary": "Map Roblox ID to Discord IDs",
        "description": "Retrieves Discord IDs associated with a given Roblox ID in a specific guild.",
        "parameters": [
          {
            "name": "robloxId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Roblox user ID."
          },
          {
            "name": "guildId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Discord guild ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response with linked Discord IDs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RobloxToDiscordResponse"
                },
                "example": {
                  "status": 200,
                  "data": {
                    "robloxId": "12345",
                    "discordIds": [
                      "67890",
                      "54321"
                    ]
                  },
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 98
                }
              }
            }
          },
          "400": {
            "description": "Missing required parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 400,
                  "error": "Missing required parameters: robloxId, guildId",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 98
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: Missing or invalid Bearer token or API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 401,
                  "error": "Unauthorized: Invalid API key",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": null,
                  "ipRequestsRemaining": null
                }
              }
            }
          },
          "403": {
            "description": "Bot is not in the specified guild.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 403,
                  "error": "Bot is not in the specified guild",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 98
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests: Rate limit exceeded or requests too frequent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                },
                "example": {
                  "status": 429,
                  "error": "Too Many Requests: Daily request limit of 500 exceeded",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "retryAfter": 3600,
                  "requestsRemaining": 0,
                  "ipRequestsRemaining": 0
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 500,
                  "error": "Internal server error",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 98
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/discord-to-roblox": {
      "get": {
        "summary": "Map Discord ID to Roblox ID",
        "description": "Retrieves the Roblox ID associated with a given Discord ID in a specific guild.",
        "parameters": [
          {
            "name": "discordId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Discord user ID."
          },
          {
            "name": "guildId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Discord guild ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response with linked Roblox ID.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DiscordToRobloxResponse"
                },
                "example": {
                  "status": 200,
                  "data": {
                    "discordId": "67890",
                    "robloxId": "12345"
                  },
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "400": {
            "description": "Missing required parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 400,
                  "error": "Missing required parameters: discordId, guildId",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: Missing or invalid Bearer token or API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 401,
                  "error": "Unauthorized: Invalid API key",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": null,
                  "ipRequestsRemaining": null
                }
              }
            }
          },
          "403": {
            "description": "Bot or user not in the specified guild.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 403,
                  "error": "User is not in the specified guild",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "404": {
            "description": "No Roblox user linked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 404,
                  "error": "No Roblox user linked to this Discord ID",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests: Rate limit exceeded or requests too frequent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                },
                "example": {
                  "status": 429,
                  "error": "Too Many Requests: Please wait 2 seconds before the next request",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "retryAfter": 2,
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 500,
                  "error": "Internal server error",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/link/update-discord": {
      "put": {
        "summary": "Update Discord ID",
        "description": "Updates the Discord ID linked to a Roblox account in a specific guild.",
        "parameters": [
          {
            "name": "oldDiscordId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The old Discord user ID."
          },
          {
            "name": "newDiscordId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The new Discord user ID."
          },
          {
            "name": "guildId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Discord guild ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Discord ID updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateResponse"
                },
                "example": {
                  "status": 200,
                  "message": "Discord ID updated successfully",
                  "data": {
                    "discordId": "67890",
                    "robloxId": "12345"
                  },
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "400": {
            "description": "Missing required parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 400,
                  "error": "Missing required parameters: oldDiscordId, newDiscordId, guildId",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: Missing or invalid Bearer token or API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 401,
                  "error": "Unauthorized: Invalid API key",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": null,
                  "ipRequestsRemaining": null
                }
              }
            }
          },
          "403": {
            "description": "Bot or new user not in the specified guild.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 403,
                  "error": "New user is not in the specified guild",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests: Rate limit exceeded or requests too frequent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                },
                "example": {
                  "status": 429,
                  "error": "Too Many Requests: Daily request limit of 500 exceeded",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "retryAfter": 3600,
                  "requestsRemaining": 0,
                  "ipRequestsRemaining": 0
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 500,
                  "error": "Internal server error",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/link/update-roblox": {
      "put": {
        "summary": "Update Roblox ID",
        "description": "Updates the Roblox ID linked to a Discord account in a specific guild.",
        "parameters": [
          {
            "name": "discordId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Discord user ID."
          },
          {
            "name": "newRobloxId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The new Roblox user ID."
          },
          {
            "name": "guildId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Discord guild ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Roblox ID updated successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateResponse"
                },
                "example": {
                  "status": 200,
                  "message": "Roblox ID updated successfully",
                  "data": {
                    "discordId": "67890",
                    "robloxId": "12345"
                  },
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "400": {
            "description": "Missing required parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 400,
                  "error": "Missing required parameters: discordId, newRobloxId, guildId",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: Missing or invalid Bearer token or API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 401,
                  "error": "Unauthorized: Invalid API key",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": null,
                  "ipRequestsRemaining": null
                }
              }
            }
          },
          "403": {
            "description": "Bot or user not in the specified guild.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 403,
                  "error": "User is not in the specified guild",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests: Rate limit exceeded or requests too frequent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                },
                "example": {
                  "status": 429,
                  "error": "Too Many Requests: Daily request limit of 500 exceeded",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "retryAfter": 3600,
                  "requestsRemaining": 0,
                  "ipRequestsRemaining": 0
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 500,
                  "error": "Internal server error",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/link/delete-roblox": {
      "delete": {
        "summary": "Remove Link",
        "description": "Removes the Roblox ID linked to a Discord ID in a specific guild.",
        "parameters": [
          {
            "name": "discordId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Discord user ID."
          },
          {
            "name": "guildId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Discord guild ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Roblox ID removed successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RemoveResponse"
                },
                "example": {
                  "status": 200,
                  "message": "Roblox ID removed successfully",
                  "data": {
                    "discordId": "67890"
                  },
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "400": {
            "description": "Missing required parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 400,
                  "error": "Missing required parameters: discordId, guildId",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: Missing or invalid Bearer token or API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 401,
                  "error": "Unauthorized: Invalid API key",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": null,
                  "ipRequestsRemaining": null
                }
              }
            }
          },
          "403": {
            "description": "Bot or user not in the specified guild.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 403,
                  "error": "User is not in the specified guild",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "404": {
            "description": "No link found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 404,
                  "error": "No link found for the provided discordId",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests: Rate limit exceeded or requests too frequent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                },
                "example": {
                  "status": 429,
                  "error": "Too Many Requests: Daily request limit of 500 exceeded",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "retryAfter": 3600,
                  "requestsRemaining": 0,
                  "ipRequestsRemaining": 0
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 500,
                  "error": "Internal server error",
                  "timestamp": "2025-09-27T20:12:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/database/reset": {
      "delete": {
        "summary": "Reset Custom Database",
        "description": "Resets the custom database by deleting all records.",
        "responses": {
          "200": {
            "description": "Custom database reset successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResetResponse"
                },
                "example": {
                  "status": 200,
                  "message": "Custom database reset successfully",
                  "data": {
                    "deletedRecords": 0
                  },
                  "timestamp": "2025-10-02T12:38:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: Missing or invalid Bearer token or API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 401,
                  "error": "Unauthorized: Invalid API key",
                  "timestamp": "2025-10-02T12:38:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": null,
                  "ipRequestsRemaining": null
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests: Rate limit exceeded or requests too frequent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                },
                "example": {
                  "status": 429,
                  "error": "Too Many Requests: Daily request limit of 500 exceeded",
                  "timestamp": "2025-10-02T12:38:56Z",
                  "version": "1.0.0",
                  "retryAfter": 3600,
                  "requestsRemaining": 0,
                  "ipRequestsRemaining": 0
                }
              }
            }
          },
          "500": {
            "description": "Internal server error or custom database model not available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 500,
                  "error": "Custom database model not available",
                  "timestamp": "2025-10-02T12:38:56Z",
                  "version": "1.0.0",
                  "requestsRemaining": 499,
                  "ipRequestsRemaining": 499
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/detections/alt": {
      "get": {
        "summary": "Alt Detection Lookup",
        "description": "Scans known login metadata to identify alternate Discord accounts usage for submitted Discord IDs, Roblox IDs, or IP addresses.",
        "parameters": [
          {
            "name": "discordId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Seed Discord ID to investigate. At least one of `discordId`, `robloxId`, or `ip` is required."
          },
          {
            "name": "guildId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Guild scope required for non-global keys so the bot can confirm membership before sharing alt data."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response with alt matches, risk, and warning metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AltVpnDetectionResponse"
                },
                "example": {
                  "status": 200,
                  "data": {
                    "target": {
                      "discordIds": [
                        "112233445566778899"
                      ],
                      "robloxIds": [
                        "7744112233"
                      ],
                      "guildId": "998877665544331122"
                    },
                    "alts": [
                      {
                        "discordId": "221133445566778800",
                        "robloxId": "66442211"
                      },
                      {
                        "discordId": "556677889900112233",
                        "robloxId": null
                      }
                    ],
                    "risk": {
                      "score": 72,
                      "label": "medium",
                      "breakdown": {
                        "overall": 72,
                        "alt": 40,
                        "vpn": 90
                      }
                    },
                    "summary": {
                      "totalAlts": 2,
                      "robloxAltIds": [
                        "66442211"
                      ]
                    },
                    "warnings": [
                      "Failed to decrypt login activity IP for 112233445566778899: [REDACTED_IP]"
                    ]
                  },
                  "timestamp": "2025-11-14T18:30:22.192Z",
                  "version": "3.4.1",
                  "requestsRemaining": 492,
                  "ipRequestsRemaining": 98
                }
              }
            }
          },
          "400": {
            "description": "Missing required parameters or invalid combination.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 400,
                  "error": "Provide at least one of discordId, robloxId, or ip",
                  "timestamp": "2025-11-14T18:30:22.192Z",
                  "version": "3.4.1",
                  "requestsRemaining": 497,
                  "ipRequestsRemaining": 497
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: Missing or invalid Bearer token or API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 401,
                  "error": "Unauthorized: Invalid API key",
                  "timestamp": "2025-11-14T18:31:03.551Z",
                  "version": "3.4.1",
                  "requestsRemaining": null,
                  "ipRequestsRemaining": null
                }
              }
            }
          },
          "403": {
            "description": "Premium subscription required or bot missing from guild scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "premiumRequired": {
                    "summary": "Premium required",
                    "value": {
                      "status": 403,
                      "error": "Premium subscription required for ALT detection",
                      "timestamp": "2025-11-14T18:31:03.551Z",
                      "version": "3.4.1",
                      "requestsRemaining": 497,
                      "ipRequestsRemaining": 497
                    }
                  },
                  "guildScope": {
                    "summary": "Bot missing from guild",
                    "value": {
                      "status": 403,
                      "error": "Bot is not in the specified guild",
                      "timestamp": "2025-11-14T18:31:03.551Z",
                      "version": "3.4.1",
                      "requestsRemaining": 497,
                      "ipRequestsRemaining": 497
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too Many Requests: Rate limit exceeded for general or IP intelligence quotas.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                },
                "example": {
                  "status": 429,
                  "error": "Too Many Requests: Daily request limit of 500 exceeded",
                  "timestamp": "2025-11-14T18:32:41.551Z",
                  "version": "3.4.1",
                  "retryAfter": 3600,
                  "requestsRemaining": 0,
                  "ipRequestsRemaining": 0
                }
              }
            }
          },
          "500": {
            "description": "Internal server error or upstream enrichment failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "status": 500,
                  "error": "Internal server error",
                  "timestamp": "2025-11-14T18:30:22.192Z",
                  "version": "3.4.1",
                  "requestsRemaining": 492,
                  "ipRequestsRemaining": 492
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/verify/session": {
      "post": {
        "summary": "Create verification session",
        "description": "Creates a PID-scoped verification session and returns a verification URL containing pid + sid.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifySessionCreateRequest"
              },
              "example": {
                "pid": "pid-yourapp",
                "clientId": "user_123",
                "guildId": "optional"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification session created or existing pending session reused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifySessionCreateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing required body fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "PID does not belong to the API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Verify request quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/verify/session/{sid}": {
      "get": {
        "summary": "Get verification session status",
        "description": "Returns instant status, or waits up to 25 seconds when the optional wait query is provided.",
        "parameters": [
          {
            "name": "sid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Verification session identifier."
          },
          {
            "name": "wait",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25
            },
            "description": "Optional long-poll wait time in seconds."
          }
        ],
        "responses": {
          "200": {
            "description": "Current verification session state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifySessionStatusResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Session not found or not owned by this API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Poll too frequent, poll cap reached, or verify quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/verify/session/{sid}/stream": {
      "get": {
        "summary": "Stream verification status via SSE",
        "description": "Opens a Server-Sent Events stream. Sends an initial status event, then complete/expired, and closes.",
        "parameters": [
          {
            "name": "sid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Verification session identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "example": "event: status\\ndata: {\"sid\":\"vsn_...\",\"pid\":\"pid-yourapp\",\"status\":\"pending\",\"result\":null}\\n\\nevent: complete\\ndata: {\"sid\":\"vsn_...\",\"pid\":\"pid-yourapp\",\"result\":{\"discordId\":\"123\",\"robloxId\":\"456\",\"linkChanged\":false,\"previousRobloxId\":null}}\\n\\n"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Session not found or not owned by this API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Verify quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "RobloxToDiscordResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              200
            ],
            "description": "The HTTP status code of the response."
          },
          "data": {
            "type": "object",
            "properties": {
              "robloxId": {
                "type": "string",
                "description": "The Roblox user ID."
              },
              "discordIds": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Array of Discord user IDs linked to the Roblox ID."
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp of the response in ISO 8601 format."
          },
          "version": {
            "type": "string",
            "example": "1.0.0",
            "description": "The API version."
          },
          "requestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Number of requests remaining in the daily quota."
          },
          "ipRequestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Remaining IP-linked quota shared across API keys detected on the same IP."
          }
        }
      },
      "DiscordToRobloxResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              200
            ],
            "description": "The HTTP status code of the response."
          },
          "data": {
            "type": "object",
            "properties": {
              "discordId": {
                "type": "string",
                "description": "The Discord user ID."
              },
              "robloxId": {
                "type": "string",
                "nullable": true,
                "description": "The linked Roblox user ID, if any."
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp of the response in ISO 8601 format."
          },
          "version": {
            "type": "string",
            "example": "1.0.0",
            "description": "The API version."
          },
          "requestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Number of requests remaining in the daily quota."
          },
          "ipRequestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Remaining IP-linked quota shared across API keys detected on the same IP."
          }
        }
      },
      "UpdateResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              200
            ],
            "description": "The HTTP status code of the response."
          },
          "message": {
            "type": "string",
            "description": "A message describing the result."
          },
          "data": {
            "type": "object",
            "properties": {
              "discordId": {
                "type": "string",
                "description": "The Discord user ID."
              },
              "robloxId": {
                "type": "string",
                "description": "The linked Roblox user ID."
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp of the response in ISO 8601 format."
          },
          "version": {
            "type": "string",
            "example": "1.0.0",
            "description": "The API version."
          },
          "requestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Number of requests remaining in the daily quota."
          },
          "ipRequestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Remaining IP-linked quota shared across API keys detected on the same IP."
          }
        }
      },
      "RemoveResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              200
            ],
            "description": "The HTTP status code of the response."
          },
          "message": {
            "type": "string",
            "description": "A message describing the result."
          },
          "data": {
            "type": "object",
            "properties": {
              "discordId": {
                "type": "string",
                "description": "The Discord user ID."
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp of the response in ISO 8601 format."
          },
          "version": {
            "type": "string",
            "example": "1.0.0",
            "description": "The API version."
          },
          "requestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Number of requests remaining in the daily quota."
          },
          "ipRequestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Remaining IP-linked quota shared across API keys detected on the same IP."
          }
        }
      },
      "ResetResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              200
            ],
            "description": "The HTTP status code of the response."
          },
          "message": {
            "type": "string",
            "description": "A message describing the result."
          },
          "data": {
            "type": "object",
            "properties": {
              "deletedRecords": {
                "type": "integer",
                "description": "The number of records deleted from the custom database."
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp of the response in ISO 8601 format."
          },
          "version": {
            "type": "string",
            "example": "1.0.0",
            "description": "The API version."
          },
          "requestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Number of requests remaining in the daily quota."
          },
          "ipRequestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Remaining IP-linked quota shared across API keys detected on the same IP."
          }
        }
      },
      "AltVpnDetectionResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              200
            ],
            "description": "The HTTP status code of the response."
          },
          "data": {
            "type": "object",
            "properties": {
              "target": {
                "type": "object",
                "properties": {
                  "discordIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Discord IDs that were inspected, including linked records pulled from the user model."
                  },
                  "robloxIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Roblox IDs connected during the lookup."
                  },
                  "guildId": {
                    "type": "string",
                    "nullable": true,
                    "description": "Guild scope applied to the lookup. Null when using a global key."
                  }
                },
                "description": "Echoes the identifiers that seeded the detection run."
              },
              "alts": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "discordId": {
                      "type": "string",
                      "description": "Associated Discord ID."
                    },
                    "robloxId": {
                      "type": "string",
                      "nullable": true,
                      "description": "Linked Roblox ID when present."
                    }
                  }
                },
                "description": "Condensed list of alternate accounts that share hashed IPs or Roblox links."
              },
              "risk": {
                "type": "object",
                "properties": {
                  "score": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Overall risk score (0-100)."
                  },
                  "label": {
                    "type": "string",
                    "description": "Risk label derived from score thresholds (e.g., low, medium, high)."
                  },
                  "breakdown": {
                    "type": "object",
                    "properties": {
                      "overall": {
                        "type": "integer",
                        "description": "Composite risk score."
                      },
                      "alt": {
                        "type": "integer",
                        "description": "Risk contribution from alternate accounts."
                      },
                      "vpn": {
                        "type": "integer",
                        "description": "Risk contribution from VPN/proxy prevalence."
                      }
                    }
                  }
                }
              },
              "summary": {
                "type": "object",
                "properties": {
                  "totalAlts": {
                    "type": "integer",
                    "description": "Number of distinct alternate Discord IDs returned."
                  },
                  "robloxAltIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Roblox IDs tied to the alternate accounts."
                  }
                }
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Operator warnings with literal IPs replaced by [REDACTED_IP]."
              }
            },
            "required": [
              "target",
              "alts",
              "risk",
              "summary"
            ]
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp of the response in ISO 8601 format."
          },
          "version": {
            "type": "string",
            "example": "3.4.1",
            "description": "The API version."
          },
          "requestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Number of standard requests remaining in the daily quota."
          },
          "ipRequestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Remaining IP-linked quota shared across API keys detected on the same IP."
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              400,
              401,
              403,
              404,
              500
            ],
            "description": "The HTTP status code of the error response."
          },
          "error": {
            "type": "string",
            "description": "A message describing the error."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp of the response in ISO 8601 format."
          },
          "version": {
            "type": "string",
            "example": "1.0.0",
            "description": "The API version."
          },
          "requestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Number of requests remaining in the daily quota."
          },
          "ipRequestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Remaining IP-linked quota shared across API keys detected on the same IP."
          }
        }
      },
      "RateLimitErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              429
            ],
            "description": "The HTTP status code of the rate limit error response."
          },
          "error": {
            "type": "string",
            "description": "A message describing the rate limit error."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The timestamp of the response in ISO 8601 format."
          },
          "version": {
            "type": "string",
            "example": "1.0.0",
            "description": "The API version."
          },
          "retryAfter": {
            "type": "integer",
            "description": "Seconds until the rate limit resets or the next request can be made."
          },
          "requestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Number of requests remaining in the daily quota."
          },
          "ipRequestsRemaining": {
            "type": "integer",
            "nullable": true,
            "description": "Remaining IP-linked quota shared across API keys detected on the same IP."
          }
        }
      },
      "VerifySessionCreateRequest": {
        "type": "object",
        "required": [
          "pid",
          "clientId"
        ],
        "properties": {
          "pid": {
            "type": "string",
            "description": "PID owned by the authenticated API key."
          },
          "clientId": {
            "type": "string",
            "description": "Developer-defined client identifier used to map completion back to your user."
          },
          "guildId": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "VerifySessionCreateResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              200
            ]
          },
          "data": {
            "type": "object",
            "properties": {
              "sid": {
                "type": "string"
              },
              "pid": {
                "type": "string"
              },
              "clientId": {
                "type": "string"
              },
              "expiresAt": {
                "type": "string",
                "format": "date-time"
              },
              "reusedExisting": {
                "type": "boolean"
              },
              "verifyUrl": {
                "type": "string"
              }
            }
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "version": {
            "type": "string"
          },
          "verifyRequestsRemaining": {
            "type": "integer",
            "nullable": true
          },
          "verifyIpRequestsRemaining": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "VerifySessionStatusResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              200
            ]
          },
          "data": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "sid": {
                    "type": "string"
                  },
                  "pid": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending",
                      "expired",
                      "cancelled"
                    ]
                  },
                  "result": {
                    "type": "null"
                  }
                }
              },
              {
                "type": "object",
                "properties": {
                  "sid": {
                    "type": "string"
                  },
                  "pid": {
                    "type": "string"
                  },
                  "result": {
                    "type": "object",
                    "properties": {
                      "discordId": {
                        "type": "string",
                        "nullable": true
                      },
                      "robloxId": {
                        "type": "string",
                        "nullable": true
                      },
                      "linkChanged": {
                        "type": "boolean",
                        "nullable": true
                      },
                      "previousRobloxId": {
                        "type": "string",
                        "nullable": true
                      }
                    }
                  }
                }
              }
            ]
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "version": {
            "type": "string"
          },
          "verifyRequestsRemaining": {
            "type": "integer",
            "nullable": true
          },
          "verifyIpRequestsRemaining": {
            "type": "integer",
            "nullable": true
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    }
  }
}