Endpoint Reference
Every endpoint listed here works with a guild API key. Each entry shows the HTTP method, the path, and an example response.
Replace :guildId with your Discord guild ID. Replace other path params with the relevant ID.
All examples show the
successenvelope. Error responses follow the shape on the Errors page.
Lookup
Lookup a Discord user
Find which Roblox account a Discord user is linked to in your guild.
GET /lookup/discord/:discordId
X-Guild-ID: YOUR_GUILD_IDResponse
{
"status": "success",
"code": 200,
"data": {
"discord_id": "123456789012345678",
"roblox_id": 261,
"roblox_username": "Shedletsky",
"verified_at": "2026-04-12T18:33:21Z"
}
}Lookup a Roblox user
The reverse direction.
GET /lookup/roblox/:robloxId
X-Guild-ID: YOUR_GUILD_IDResponse
{
"status": "success",
"code": 200,
"data": {
"discord_id": "123456789012345678",
"roblox_id": 261,
"roblox_username": "Shedletsky",
"verified_at": "2026-04-12T18:33:21Z"
}
}Batch lookup
Look up many users in one call. Pass an array of either Discord or Roblox IDs.
POST /lookup/status{
"guild_id": "YOUR_GUILD_ID",
"discord_ids": ["123456789012345678", "234567890123456789"]
}Response
{
"status": "success",
"code": 200,
"data": {
"results": [
{ "discord_id": "123456789012345678", "verified": true, "roblox_id": 261 },
{ "discord_id": "234567890123456789", "verified": false }
]
}
}Reverse lookup
Same as batch lookup but always returns the full link records.
POST /reverse{ "guild_id": "YOUR_GUILD_ID", "roblox_ids": [261] }Response
{
"status": "success",
"code": 200,
"data": {
"results": [
{ "roblox_id": 261, "discord_id": "123456789012345678", "roblox_username": "Shedletsky" }
]
}
}Bindings (Roblox group rank to Discord role)
List bindings
GET /bindings/list
X-Guild-ID: YOUR_GUILD_IDResponse
{
"status": "success",
"code": 200,
"data": {
"bindings": [
{
"id": 12,
"type": "group_rank",
"group_id": 7384293,
"rank": 200,
"role_id": "987654321098765432"
}
]
}
}Add binding
POST /bindings/add{
"guild_id": "YOUR_GUILD_ID",
"type": "group_rank",
"group_id": 7384293,
"rank": 200,
"role_id": "987654321098765432"
}Response
{
"status": "success",
"code": 200,
"message": "Binding created",
"data": { "id": 12 }
}Update binding
PATCH /bindings/update{ "binding_id": 12, "rank": 250 }Response
{ "status": "success", "code": 200, "message": "Binding updated" }Remove binding
DELETE /bindings/remove/:bindingIdResponse
{ "status": "success", "code": 200, "message": "Binding removed" }Test a binding
Dry-run a binding spec against a user without saving it.
POST /bindings/test{
"guild_id": "YOUR_GUILD_ID",
"discord_id": "123456789012345678",
"type": "group_rank",
"group_id": 7384293,
"rank": 200
}Response
{
"status": "success",
"code": 200,
"data": { "matches": true, "current_rank": 250 }
}Validate a binding
Check that a binding spec is well-formed before saving.
POST /bindings/validate{ "type": "group_rank", "group_id": 7384293, "rank": 200, "role_id": "987654321098765432" }Response
{ "status": "success", "code": 200, "data": { "valid": true } }Sync (re-evaluate a user's roles)
Sync one user
POST /interactions/sync{ "guild_id": "YOUR_GUILD_ID", "user_id": "123456789012345678" }Response
{
"status": "success",
"code": 200,
"data": {
"added_roles": ["987654321098765432"],
"removed_roles": [],
"nickname_updated": true
}
}Sync all users
Triggers a full guild re-evaluation. Very heavy. Limited to 1 call per hour per guild.
POST /interactions/sync/all{ "guild_id": "YOUR_GUILD_ID" }Response
{
"status": "success",
"code": 200,
"data": { "queued": true, "members": 4128 }
}Assign a role manually
POST /interactions/roles/assign{ "guild_id": "YOUR_GUILD_ID", "user_id": "123...", "role_id": "987..." }Response
{ "status": "success", "code": 200, "message": "Role assigned" }Remove a role manually
POST /interactions/roles/remove{ "guild_id": "YOUR_GUILD_ID", "user_id": "123...", "role_id": "987..." }Response
{ "status": "success", "code": 200, "message": "Role removed" }Guild basics
List guild roles
The Discord roles in your guild. Useful for picking the right role for a binding.
GET /guilds/:guildId/rolesResponse
{
"status": "success",
"code": 200,
"data": {
"roles": [
{ "id": "987654321098765432", "name": "Verified", "color": 5763719, "position": 12 }
]
}
}List guild members (verified)
GET /guilds/:guildId/membersResponse
{
"status": "success",
"code": 200,
"data": {
"members": [
{ "discord_id": "123...", "roblox_id": 261, "roblox_username": "Shedletsky" }
],
"total": 4128
}
}List verified users
Shorter version of the above. Just verified records.
GET /guilds/:guildId/verified-usersResponse
{
"status": "success",
"code": 200,
"data": [
{ "discord_id": "123...", "roblox_id": 261, "roblox_username": "Shedletsky" }
]
}Audit logs
List audit log entries
GET /guilds/:guildId/audit-logsOptional query params: ?limit=50&cursor=<id>&action=<type>.
Response
{
"status": "success",
"code": 200,
"data": {
"entries": [
{
"id": 9012,
"action": "ban.create",
"actor_id": "123...",
"target_id": "234...",
"metadata": { "reason": "Exploiting" },
"created_at": "2026-05-08T14:22:01Z"
}
],
"next_cursor": null
}
}Write a custom audit entry
Your own integration can append entries. Useful for surfacing third-party actions in the dashboard log feed.
POST /guilds/:guildId/audit-logs{ "action": "custom.deploy", "metadata": { "build": "v1.42.0" } }Response
{ "status": "success", "code": 200, "data": { "id": 9013 } }Moderation (game-server hot path)
These are the calls a game server makes most often. They check a single user fast, with an aggressive cache layer.
Check ban status
GET /moderation/:guildId/ban-status/:identifier:identifier is either a Discord ID (string) or Roblox user ID (number). Optional query: ?place_id=<placeId> to scope to a specific game.
Response (banned)
{
"status": "success",
"code": 200,
"message": "User is banned",
"data": {
"banned": true,
"ban_info": {
"id": 1247,
"reason": "Exploiting",
"moderator_id": "123...",
"expires_at": null,
"created_at": "2026-04-12T18:33:21Z"
}
}
}Response (not banned)
{
"status": "success",
"code": 200,
"message": "User is not banned",
"data": { "banned": false }
}Check mute status
GET /moderation/:guildId/mute-status/:identifierResponse (muted)
{
"status": "success",
"code": 200,
"data": {
"muted": true,
"mute_info": {
"id": 412,
"reason": "Spam",
"expires_at": "2026-05-09T00:00:00Z"
}
}
}List active bans
Use this on game-server start to seed your local cache.
GET /moderation/:guildId/roblox/bans/activeResponse
{
"status": "success",
"code": 200,
"data": {
"bans": [
{ "id": 1247, "identifier": "261", "reason": "Exploiting", "expires_at": null }
]
}
}List active mutes
GET /moderation/:guildId/roblox/mutes/activeResponse
{
"status": "success",
"code": 200,
"data": {
"mutes": [
{ "id": 412, "identifier": "261", "reason": "Spam", "expires_at": "2026-05-09T00:00:00Z" }
]
}
}Push a ban from the game server
When a moderator bans someone in-game, mirror it back to Technified so the dashboard and Discord side stay in sync.
POST /moderation/:guildId/roblox/ban{
"identifier": "261",
"reason": "Exploiting",
"moderator_id": "123456789012345678",
"duration_seconds": null,
"place_id": 1818
}Response
{
"status": "success",
"code": 200,
"data": { "id": 1247, "banned": true }
}Push an unban
POST /moderation/:guildId/roblox/unban{ "identifier": "261", "moderator_id": "123..." }Response
{ "status": "success", "code": 200, "data": { "unbanned": true } }Push a mute
POST /moderation/:guildId/roblox/mute{
"identifier": "261",
"reason": "Spam",
"moderator_id": "123...",
"duration_seconds": 3600
}Response
{ "status": "success", "code": 200, "data": { "id": 412, "muted": true } }Push an unmute
POST /moderation/:guildId/roblox/unmute{ "identifier": "261", "moderator_id": "123..." }Response
{ "status": "success", "code": 200, "data": { "unmuted": true } }Technified Shield (cross-guild flagged users)
Any guild API key can read the Shield list. Only allowlisted guilds can write to it.
Check one user
GET /shield/check/:robloxIdResponse (flagged)
{
"status": "success",
"code": 200,
"data": {
"flagged": true,
"flag": {
"roblox_id": "261",
"roblox_username": "Shedletsky",
"reason": "User is part of a condo group (12345678)",
"flagged_by": "Technified",
"source": "manual",
"flagged_at": "2026-04-12T18:33:21Z"
}
}
}Response (clean)
{ "status": "success", "code": 200, "data": { "flagged": false } }Check up to 200 users at once
POST /shield/batch{ "roblox_ids": [261, 1, 156] }Response
{
"status": "success",
"code": 200,
"data": {
"flagged": [
{ "roblox_id": "261", "roblox_username": "Shedletsky", "reason": "User is part of a condo group (12345678)", "source": "manual" }
]
}
}Bulk import (allowlisted guilds only)
POST /shield/import{
"users": [
{ "roblox_id": 261, "reason": "User is part of a condo group (12345678)" }
]
}Response
{ "status": "success", "code": 200, "data": { "imported": 1, "skipped": 0 } }Staff (basic)
List staff members
GET /staff/members/:guildIdResponse
{
"status": "success",
"code": 200,
"data": {
"members": [
{
"discord_id": "123...",
"roblox_id": 261,
"role_id": "987...",
"role_name": "Moderator",
"on_duty": false
}
]
}
}Get one staff member's permissions
GET /staff/permissions/:guildId/:userIdResponse
{
"status": "success",
"code": 200,
"data": {
"user_id": "123...",
"role_name": "Moderator",
"permissions": ["moderation.read", "moderation.write", "lookup.read"]
}
}Get staff profile
Combined info for one staff member.
GET /guilds/:guildId/staff/profile/:userIdResponse
{
"status": "success",
"code": 200,
"data": {
"discord_id": "123...",
"role_name": "Moderator",
"joined_at": "2025-09-12T08:00:00Z",
"actions_total": 482,
"sessions_total": 73,
"minutes_active": 4811,
"open_loa": null,
"active_strikes": 0
}
}Recent actions
GET /staff/activity/:guildIdOptional ?limit=50&cursor=<id>.
Response
{
"status": "success",
"code": 200,
"data": {
"actions": [
{
"id": 9012,
"staff_id": "123...",
"action_type": "ban",
"target_id": "261",
"created_at": "2026-05-08T14:22:01Z"
}
],
"next_cursor": null
}
}Aggregate stats
GET /staff/stats/:guildIdResponse
{
"status": "success",
"code": 200,
"data": {
"total_actions": 12048,
"total_sessions": 1421,
"total_minutes": 89321,
"by_type": { "ban": 482, "mute": 1207, "warn": 3091 }
}
}Leaderboard
GET /staff/leaderboard/:guildIdOptional ?period=week|month|all.
Response
{
"status": "success",
"code": 200,
"data": {
"period": "week",
"leaderboard": [
{ "discord_id": "123...", "role_name": "Moderator", "score": 412, "actions": 38 }
]
}
}List action types
The set of action_type values used by the activity log.
GET /staff/action-typesResponse
{
"status": "success",
"code": 200,
"data": ["ban", "unban", "mute", "unmute", "warn", "kick", "custom"]
}Staff: LOA (Leave of Absence)
List LOAs
GET /guilds/:guildId/staff/loaResponse
{
"status": "success",
"code": 200,
"data": {
"loas": [
{
"id": 71,
"staff_id": "123...",
"starts_at": "2026-05-10T00:00:00Z",
"ends_at": "2026-05-17T00:00:00Z",
"reason": "Vacation",
"status": "active"
}
]
}
}Create LOA
POST /guilds/:guildId/staff/loa{
"staff_id": "123...",
"starts_at": "2026-05-10T00:00:00Z",
"ends_at": "2026-05-17T00:00:00Z",
"reason": "Vacation"
}Response
{ "status": "success", "code": 200, "data": { "id": 71 } }Update / delete LOA
PATCH /guilds/:guildId/staff/loa/:loaId
DELETE /guilds/:guildId/staff/loa/:loaIdResponse
{ "status": "success", "code": 200, "message": "LOA updated" }Expire finished LOAs
Sweeps any LOA whose ends_at is in the past and marks it expired.
POST /guilds/:guildId/staff/loa/expireResponse
{ "status": "success", "code": 200, "data": { "expired": 3 } }Staff: Strikes
List strikes
GET /guilds/:guildId/staff/strikesResponse
{
"status": "success",
"code": 200,
"data": {
"strikes": [
{
"id": 41,
"staff_id": "123...",
"reason": "Missed quota",
"severity": 1,
"expires_at": "2026-08-08T00:00:00Z",
"active": true
}
]
}
}Create strike
POST /guilds/:guildId/staff/strikes{ "staff_id": "123...", "reason": "Missed quota", "severity": 1 }Response
{ "status": "success", "code": 200, "data": { "id": 41 } }Update / remove / delete strike
PATCH /guilds/:guildId/staff/strikes/:strikeId/update
PATCH /guilds/:guildId/staff/strikes/:strikeId
DELETE /guilds/:guildId/staff/strikes/:strikeIdPATCH .../update edits fields. The plain PATCH removes the strike (sets active=false). DELETE wipes it.
Response
{ "status": "success", "code": 200, "message": "Strike removed" }Expire old strikes
POST /guilds/:guildId/staff/strikes/expireResponse
{ "status": "success", "code": 200, "data": { "expired": 2 } }Staff: Notes
List, create, update, delete
GET /guilds/:guildId/staff/notes
POST /guilds/:guildId/staff/notes
PATCH /guilds/:guildId/staff/notes/:noteId
DELETE /guilds/:guildId/staff/notes/:noteId{ "staff_id": "123...", "body": "Trial period extended by 2 weeks" }Response
{
"status": "success",
"code": 200,
"data": {
"id": 18,
"staff_id": "123...",
"body": "Trial period extended by 2 weeks",
"author_id": "234...",
"created_at": "2026-05-08T14:22:01Z"
}
}Staff: Quota
List quotas
GET /guilds/:guildId/staff/quotaResponse
{
"status": "success",
"code": 200,
"data": {
"quotas": [
{
"id": 4,
"name": "Monthly bans",
"metric": "ban",
"target": 20,
"period": "month",
"scope": "role:Moderator"
}
]
}
}Create / update / delete quota
POST /guilds/:guildId/staff/quota
PATCH /guilds/:guildId/staff/quota/:quotaId
DELETE /guilds/:guildId/staff/quota/:quotaId{ "name": "Monthly bans", "metric": "ban", "target": 20, "period": "month" }Response
{ "status": "success", "code": 200, "data": { "id": 4 } }Quota progress
GET /guilds/:guildId/staff/quota/:quotaId/progressResponse
{
"status": "success",
"code": 200,
"data": {
"quota_id": 4,
"rows": [
{ "staff_id": "123...", "current": 12, "target": 20, "percent": 60 }
]
}
}Pending actions
Some quotas need approval before they count.
GET /guilds/:guildId/staff/quota-pending
POST /guilds/:guildId/staff/quota-pending/:actionId/approve
POST /guilds/:guildId/staff/quota-pending/:actionId/rejectResponse
{
"status": "success",
"code": 200,
"data": {
"pending": [
{ "id": 88, "staff_id": "123...", "action_type": "custom", "submitted_at": "2026-05-08T14:00:00Z" }
]
}
}Roblox activity tracking
Drive these from your game's server scripts. They power the staff activity dashboard.
Verify in-game staff
Confirms a Roblox player is staff before granting in-game tools.
POST /staff/roblox/verify{ "guild_id": "YOUR_GUILD_ID", "roblox_id": 261 }Response
{
"status": "success",
"code": 200,
"data": {
"is_staff": true,
"discord_id": "123...",
"role_name": "Moderator",
"permissions": ["kick", "ban", "warn"]
}
}Register a game (universe)
Call once per universe so the dashboard can show its name and place ID.
POST /roblox/games/register{ "guild_id": "YOUR_GUILD_ID", "place_id": 1818, "universe_id": 121, "name": "My Game" }Response
{ "status": "success", "code": 200, "data": { "registered": true } }Start a session
Call when a staff member joins the server.
POST /staff/roblox/session/start{ "guild_id": "YOUR_GUILD_ID", "roblox_id": 261, "place_id": 1818, "server_id": "abc-123" }Response
{
"status": "success",
"code": 200,
"data": { "session_id": "sess_abc123", "started_at": "2026-05-08T14:22:01Z" }
}End a session
POST /staff/roblox/session/end{ "session_id": "sess_abc123" }Response
{
"status": "success",
"code": 200,
"data": { "session_id": "sess_abc123", "duration_seconds": 1820 }
}Heartbeat
Send every 30 seconds to keep the session alive.
POST /staff/roblox/heartbeat{ "session_id": "sess_abc123" }Response
{ "status": "success", "code": 200, "data": { "alive": true } }Log a custom action
POST /staff/roblox/action{
"guild_id": "YOUR_GUILD_ID",
"session_id": "sess_abc123",
"action_type": "custom",
"target_id": "261",
"details": { "command": "/freeze" }
}Response
{ "status": "success", "code": 200, "data": { "id": 9012 } }Get a Roblox user's permissions in-game
Useful when a player runs an admin command and you need the permission tier.
GET /guilds/:guildId/staff/roblox/:robloxId/permissionsResponse
{
"status": "success",
"code": 200,
"data": {
"roblox_id": 261,
"is_staff": true,
"role_name": "Moderator",
"permissions": ["kick", "ban", "warn"]
}
}Server Manager (live commands)
The dashboard can queue commands and your game server polls for them.
Heartbeat
POST /roblox/servers/heartbeat{
"server_id": "abc-123",
"place_id": 1818,
"universe_id": 121,
"player_count": 14
}Response
{ "status": "success", "code": 200, "data": { "ok": true } }Poll for queued commands
GET /roblox/servers/:serverId/commandsResponse
{
"status": "success",
"code": 200,
"data": {
"commands": [
{
"id": "cmd_42",
"type": "kick",
"target_id": "261",
"reason": "Exploiting",
"issued_at": "2026-05-08T14:22:01Z"
}
]
}
}Acknowledge a command
Call after you have applied the command in-game.
POST /roblox/servers/:serverId/commands/:commandId/ack{ "status": "ok" }Response
{ "status": "success", "code": 200, "data": { "acked": true } }Tickets
Create a ticket from an application submission
Triggered when an applicant gets to a stage that needs a private channel.
POST /guilds/:guildId/application-submissions/:submissionId/create-ticket{ "panel_id": 7 }Response
{
"status": "success",
"code": 200,
"data": { "ticket_id": 941, "channel_id": "1098765432109876543" }
}WebSocket (game server)
Heads up. Roblox does not currently allow outgoing WebSocket connections from live game servers. This endpoint only works from Roblox Studio, not from a published game. The integration is already wired up on our side, so as soon as Roblox enables WebSockets in production it will work without any code change. For now, use HTTP polling on /roblox/servers/:serverId/commands instead.
Live channel for game-server events. Open with Upgrade: websocket. The API key must match :guildId.
GET /ws/guilds/:guildId/roblox?placeId=1818&serverId=abc-123&universeId=121After upgrade you get JSON frames like:
{ "type": "moderation.ban", "data": { "identifier": "261", "reason": "Exploiting" } }{ "type": "command", "data": { "id": "cmd_42", "type": "kick", "target_id": "261" } }The dashboard equivalent (/ws/guilds/:guildId/live) uses Discord OAuth, not API keys, so it isn't listed here.