Siirry sisältöön

Mizan API

Check every player who joins your server against every ban list Mizan reads, and add your own bans. JSON over HTTPS, one key per organization.

Base URLhttps://api.mizan.ac

Start here

Not writing a server integration? The same records come as each player page's .json and .md, from the MCP server at mizan.ac/mcp with no sign-in, and from the Discord bot. What Mizan returns and how to get it covers each one and its limits.

Overview

Your server asks Mizan about each player as they connect. The answer says whether a list banned them for cheating, naming each list. It also gives how many bans they have, from how many communities, and their full public record. What to do with it is your call: Mizan doesn't kick or ban anyone for you.

  • Check one player as they join: GET /v1/players/{player}
  • Check up to 1,000 players at once, such as everyone on a server or a page of players: POST /v1/players/batch
  • Keep your own copy of the registry's bans for players you track: check them once with the batch, then follow GET /v1/bans/changes from its cursor; it gives every ban created, updated, ended or removed since, in order
  • Read Steam's own record of one account (VAC, game, community and trade bans, and each ban's game when Steam gives it): GET /v1/steam/{player}
  • Add your own bans, or update one by sending it again: POST /v1/bans
  • End one of your bans after an unban or an appeal: DELETE /v1/bans/{ref}
  • Refuse players with a cheating ban in force on a Rust server: set server.bansServerEndpoint to https://api.mizan.ac/v1/rust/{key}/ (setup)
  • Read the registry's size, every list it reads and one player's public record, with no key: GET /v1/stats, GET /v1/sources and GET /v1/public/players/{player}

A success holds its answer under data, and every time is an ISO 8601 string in UTC. Every player answer carries Steam's record as steam.

Caching

Keyed reads by GET answer Cache-Control: private, max-age=60, stale-while-revalidate=300, an ETag and Vary: X-API-Key, Authorization. Keep the answer up to a minute, then send its ETag back as If-None-Match: an unchanged answer comes back as 304 Not Modified with no body. A player's answer is never more than a minute old on Mizan's side either, so asking sooner gains nothing. private means a shared cache (a CDN, or your own Worker's cache in front of many visitors) must not keep it, because it was read with your key. Public routes answer public with their own ages and ETags.

Authentication

Routes that need a key take your organization's API key in the X-API-Key header. Keys made on mizan.ac start with mzn_; keys made earlier on justified.gg start with jak_ and still work.

Keep the key on your game server or backend, never in anything players download. Whoever holds it can read records as your organization and, if your organization contributes, add bans in its name.

An approved organization can read records. Adding bans needs contributor status, which GET /v1/me reports along with your servers. A missing or revoked key gets 401; an organization still waiting for approval, or suspended, gets 403.

A key you turned off answers 401 with the code KEY_DISABLED, an expired one KEY_EXPIRED, and one that has used all its requests USAGE_EXCEEDED. A key set to read only reads normally and gets 403 KEY_NOT_ALLOWED when it adds or ends bans, and a key limited to some of your servers gets 400 naming server for any other. A server limit sets which servers a key acts for (reporting joins, adding and ending bans), not which of your organization's bans it sees: your bans apply on all your servers. Such a key can't change a ban another of your servers made: an entry naming that player and ref is left as it is and listed by its path in the answer's refused, and the rest of the batch is written. On a read, KEY_NOT_ALLOWED means the key has no read access at all. Changes you make to a key on mizan.ac reach the API within a minute.

Rate limits

Each limit counts requests over one minute.

RequestsA minuteCounted per
Public routes (stats, sources and public records)600IP address (IPv6: its /64)
Public requests the edge cache can't answer120IP address (IPv6: its /64)
Requests with a missing or refused key, or a key Mizan has not seen yet120IP address (IPv6: its /64), or the calling zone for a Cloudflare Worker
Routes that take a key300organization
Adding and ending bans30organization
A key with its own limit set on mizan.ac30, 60 or 120key

A Cloudflare Worker on another zone calling the API is counted by its zone (Cloudflare's CF-Worker header), not by the one address Cloudflare gives every Worker. The organization's limits apply to all of its keys together. A key's own limit, when you set one, is counted first, by that key alone, so requests it refuses don't use up the organization's limits. Each limit is counted separately in each Cloudflare location and is approximate.

Past a limit, the API answers 429 with the code RATE_LIMITED and a Retry-After header in seconds, and its RateLimit header names the limit that ran out, such as "origin";r=0;t=10 or "key_60";r=0;t=10. Every answer names the limits it counts against in a RateLimit-Policy header.

Checking a full server or a page of players? Send up to 1,000 in one POST /v1/players/batch rather than a request each: every 100 players count as one request against the limits, so a batch costs what the same players would cost one by one, in one round trip. A player's answer can be up to a minute old, so asking about the same player again sooner gains nothing. Tracking thousands of players continuously? Don't poll them: follow GET /v1/bans/changes, which costs one request a page however many players you track.

Answers are compressed (brotli or gzip) whenever your client sends Accept-Encoding, which most HTTP clients do by default: a 1,000-player batch is about 320 KB of JSON and about 17 KB on the wire.

Errors

A refusal has an HTTP status and a body with one error object. Branch on code, which is fixed; message is written for people and can change. Every refusal names its request as requestId, the same as its X-Request-Id header; when you report a problem, quote the X-Request-Id header from the answer.

JSON
{
  "error": {
    "code": "INVALID_PLAYER_ID",
    "message": "A player id is the game's own id for the player, such as the SteamID64 76561197960276590, optionally as steam:76561197960276590.",
    "target": "player"
  }
}
400
The request is malformed: a bad player id, server key or body.
401
No key, or one that is unknown, disabled or revoked.
403
Your organization is waiting for approval or is suspended, or cannot add bans.
404
No such player, ban or route.
409
conflict: several of your open bans have that ref; name the ban by its id.
410
Code cursor_expired: the cursor is more than 29 days old. Check your players again with the batch, then start over without after.
412
precondition_failed: the policy changed since you read it. Read it again.
413
The body is over 4 MiB.
415
Send the body as application/json.
422
idempotency_key_reused: that key came with another request.
429
Rate limited. Wait a few seconds.
503
The data behind the route is unavailable right now.

Registry

The registry: its size, the lists it reads, a player's record, and your own bans.

Get the registry's size

GET/v1/statsNo key

How many ban lists the registry reads, the bans on them, how many are active, and the games they cover. Updated every minute. With game, only that game's bans and the lists that hold them.

Query

gamestring

Answer

data.listsinteger
data.bansinteger
data.activeBansinteger
data.gamesinteger

Can refuse with 429. What each means

curl "https://api.mizan.ac/v1/stats"
Live answer
{
  "data": {
    "bans": 5054326,
    "games": 21,
    "lists": 568,
    "activeBans": 4453748
  }
}
Read from the live API on 31. lokakuuta 2026 klo 04.50 UTC.

Calls the live API from your browser. No key needed.

List the ban lists the registry reads

GET/v1/sourcesNo key

Largest first. state is reading, retrying (the list refused or failed its last reads, so it is read less often) or stopped, as of the hour's count, like the bans; /v1/public/registry/status gives it live for every list, with bans or not. url is the list's public page, when it has one. kind says who issues the list's bans: an anti-cheat's detections, one community's staff, the game's developer (platform) or a list several communities share (community).

Answer

data[].keystring
data[].namestring
data[].kindstring or null
Who issues the list's bans; null until it is classified. One of anti_cheat, staff, platform, community, null.
data[].urlstring or null
data[].gamesarray of strings
data[].bansinteger
data[].activeBansinteger
data[].statestring
One of reading, retrying, stopped.
data[].changedAtdate-time or null
data[].scheduleobject or null
data[].schedule.firstPageHourlyboolean
data[].schedule.firstPageEverySecondsinteger or null
Seconds between checks of the list's first page, set by how often it bans: from 300 for a busy list to 3600.
data[].schedule.wholeListEverySecondsinteger

Can refuse with 429. What each means

curl "https://api.mizan.ac/v1/sources"
Live answer
{
  "data": [
    {
      "key": "external:phishdestroy-archiasf",
      "bans": 1200011,
      "name": "Steam bans, PhishDestroy's archiasf dossier (2026-09-24)",
      "games": [],
      "state": "reading",
      "changedAt": "2026-10-30T02:09:08.000Z",
      "activeBans": 1200011,
      "kind": null,
      "url": "https://phishdestroy.io",
      "schedule": {
        "firstPageHourly": false,
        "firstPageEverySeconds": null,
        "wholeListEverySeconds": 2592000
      }
    },
    {
      "key": "external:rust-admin",
      "bans": 424768,
      "name": "rust-admin.ru shared list",
      "games": [
        "rust"
      ],
      "state": "reading",
      "changedAt": "2026-10-31T01:41:00.000Z",
      "activeBans": 361349,
      "kind": "community",
      "url": "https://rust-admin.ru",
      "schedule": {
        "firstPageHourly": true,
        "firstPageEverySeconds": 300,
        "wholeListEverySeconds": 604800
      }
    }
  ],
  "total": 568
}
Read from the live API on 31. lokakuuta 2026 klo 04.50 UTC. The first 2 of 568 rows.

Calls the live API from your browser. No key needed.

Check up to 1,000 players

POST/v1/players/batchNeeds a key

Each player's summary and Steam's record, as a map keyed by each id exactly as you sent it, so you match answers to your own ids in any form (SteamID64, steam:..., STEAM_0:..., [U:1:...], a Minecraft UUID ...). Send ids as strings: a SteamID64 is past what a JSON number holds exactly. An id that is not a player id is listed in invalid and the rest are still answered; an id sent twice is answered once. With server, a bare id is read the way that server's game names players. Every 100 players count as one request against the rate limits. The map's keys come in no promised order.

Body

playersarray of stringsRequired
1 to 1,000 player ids. At most 1000.
serverstring
One of your servers, by its key.

Can refuse with 400, 401, 403, 429, 503. What each means

curl -X POST "https://api.mizan.ac/v1/players/batch" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"players":["76561197960287930","STEAM_0:1:19867137","[U:1:22202]","not-an-id"]}'
Answer
{
  "data": {},
  "invalid": [
    "string"
  ]
}

Check one player

GET/v1/players/{player}Key optional

With a key (players.view): the player's summary and public record, every list's bans on them with Mizan's own marks, and Steam's own record as steam with steamState saying where it stands, Cache-Control: private. Without a key: the public view /v1/public/players/{player} answers, each list's bans counted and the record without labels or Steam's record, Cache-Control: public, max-age=60, counted against the public limits by address. The record carries up to 200 bans; GET /v1/players/{player}/bans pages through all of them.

Path

playerstringRequired
Any game's player: provider:id (steam, minecraft, fivem, epic, xbox, discord), or without a prefix a Steam id in any form (SteamID64, SteamID2 STEAM_0:0:11101, SteamID3 [U:1:22202], the account id 22202, a steamcommunity.com/profiles/ link), a Minecraft UUID with or without dashes, or a FiveM license: hash. With server, a bare id is read the way that server's game names players. URL-encode SteamID3's brackets.

Query

serverstring
One of your servers, by its key.

Answer

data.playerobject
data.player.providerstring
data.player.idstring
data.profilestring
data.cheaterboolean
Banned for cheating by any organization.
data.trollingboolean
Has a conduct ban (RDM, NITRP, prop abuse, toxicity and the like) from any organization that is in force or was issued in the last year.
data.mingeboolean
The same value as trolling, kept for clients that read it. Read trolling instead.
data.bansobject
data.bans.totalinteger
data.bans.activeinteger
data.bans.sourcesinteger
How many organizations' lists name them.
data.bans.activeSourcesinteger
How many of those lists have a ban on them in force.
data.bans.conductinteger
Bans for conduct, of any age.
data.bans.categoriesarray of strings
data.bans.gamesarray of strings
The games of the bans that name one.
data.bans.lastAtdate-time or null
data.recordobject or null
data.steamvalue

Can refuse with 400, 401, 403, 404, 429, 503. What each means

curl "https://api.mizan.ac/v1/players/76561197960287930"
Answer
{
  "data": {
    "player": {
      "provider": "steam",
      "id": "76561197960287930"
    },
    "profile": "https://mizan.ac/players/steam/76561197960287930",
    "cheater": false,
    "trolling": false,
    "minge": false,
    "bans": {
      "total": 0,
      "active": 0,
      "sources": 0,
      "activeSources": 0,
      "conduct": 0,
      "categories": [
        "MRDM"
      ],
      "games": [
        "garrysmod"
      ],
      "lastAt": "2026-10-09T21:40:00.000Z"
    },
    "record": null,
    "steam": null
  }
}

Check up to 100 players

POST/v1/players/lookupNeeds a keyDeprecated

Answers with Deprecation and Link: rel="deprecation" headers (RFC 9745). Kept for callers already using it; new code uses POST /v1/players/batch, which takes ten times as many players and never refuses the batch for one id. Up to 100 players, answered as a list in the order sent, each player once. One id that is not a player id refuses the batch with 400 naming it (players[3]).

Body

playersarray of stringsRequired
At most 100.
serverstring
One of your servers, by its key.

Answer

data[].playerobject
data[].player.providerstring
data[].player.idstring
data[].profilestring
data[].cheaterboolean
Banned for cheating by any organization.
data[].trollingboolean
Has a conduct ban (RDM, NITRP, prop abuse, toxicity and the like) from any organization that is in force or was issued in the last year.
data[].mingeboolean
The same value as trolling, kept for clients that read it. Read trolling instead.
data[].bansobject
data[].bans.totalinteger
data[].bans.activeinteger
data[].bans.sourcesinteger
How many organizations' lists name them.
data[].bans.activeSourcesinteger
How many of those lists have a ban on them in force.
data[].bans.conductinteger
Bans for conduct, of any age.
data[].bans.categoriesarray of strings
data[].bans.gamesarray of strings
The games of the bans that name one.
data[].bans.lastAtdate-time or null
data[].steamvalue

Can refuse with 400, 401, 403, 404, 429, 503. What each means

curl -X POST "https://api.mizan.ac/v1/players/lookup" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"server":"darkrp-1","players":["76561197960287930","STEAM_0:1:19867137","[U:1:22202]"]}'
Answer
{
  "data": [
    {
      "player": {
        "provider": "steam",
        "id": "76561197960287930"
      },
      "profile": "https://mizan.ac/players/steam/76561197960287930",
      "cheater": false,
      "trolling": false,
      "minge": false,
      "bans": {
        "total": 0,
        "active": 0,
        "sources": 0,
        "activeSources": 0,
        "conduct": 0,
        "categories": [
          "MRDM"
        ],
        "games": [
          "garrysmod"
        ],
        "lastAt": "2026-10-09T21:40:00.000Z"
      },
      "steam": null
    }
  ]
}

Read one player's public record

GET/v1/public/players/{player}No key

No key needed. The player's bans counted by list, how many are in force, and their public record (every list's bans and the names the lists recorded). A ban is the claim of the list that made it, so this answer counts and credits bans and never labels the player. Steam's own record is left out; /v1/players/{player} has it. Held a minute.

Path

playerstringRequired
Any game's player: provider:id (steam, minecraft, fivem, epic, xbox, discord), or without a prefix a Steam id in any form (SteamID64, SteamID2 STEAM_0:0:11101, SteamID3 [U:1:22202], the account id 22202, a steamcommunity.com/profiles/ link), a Minecraft UUID with or without dashes, or a FiveM license: hash. URL-encode SteamID3's brackets.

Answer

data.playerobject
data.player.providerstring
data.player.idstring
data.profilestring
data.caughtboolean
Always false: Mizan makes no detections of its own, and a list's ban never sets this.
data.listsobject
data.lists.totalinteger
Lists with a ban on the player.
data.lists.inForceinteger
Of those, the lists with a ban in force.
data.lists.endedinteger
Of those, the lists whose bans have all ended or expired.
data.bansobject
data.bans.totalinteger
data.bans.inForceinteger
data.bans.endedinteger
data.bans.cheatingobject
Bans each list labelled cheating, counted with the lists that made them.
data.bans.conductobject
Bans each list labelled for conduct, counted with the lists that made them. Of any age.
data.bans.categoriesarray of strings
Every offence the lists name, as ban category keys.
data.bans.gamesarray of strings
The games of the bans that name one.
data.bans.lastAtdate-time or null
data.recordobject or null
The public record: every list's bans, each naming its list (bans[].source), and the names the lists recorded.

Can refuse with 400, 429, 503. What each means

curl "https://api.mizan.ac/v1/public/players/76561197960287930"
Answer
{
  "data": {
    "player": {
      "provider": "steam",
      "id": "76561197960287930"
    },
    "profile": "https://mizan.ac/players/steam/76561197960287930",
    "caught": false,
    "lists": {
      "total": 0,
      "inForce": 0,
      "ended": 0
    },
    "bans": {
      "total": 0,
      "inForce": 0,
      "ended": 0,
      "cheating": {
        "total": 0,
        "inForce": 0,
        "lists": 0,
        "listsInForce": 0
      },
      "conduct": {
        "total": 0,
        "inForce": 0,
        "lists": 0,
        "listsInForce": 0
      },
      "categories": [
        "MRDM"
      ],
      "games": [
        "garrysmod"
      ],
      "lastAt": "2026-10-09T21:40:00.000Z"
    },
    "record": null
  }
}

Calls the live API from your browser. No key needed.

Answer a Rust server's ban check

GET/v1/rust/{key}/{player}No key

For Rust's centralized banning (wiki.facepunch.com/rust/centralized-banning). Set server.bansServerEndpoint to this path up to and including your key, ending in /; the server adds each joining player's SteamID64. The key is in the path because the convar cannot send headers. A player is refused only while a list's ban labelled cheating is in force on them; the reason names the lists and where to dispute it. When the API cannot check a player (an outage, or your organization's limit), it answers 404 with X-Mizan-Unchecked and the player joins. Answers can be a minute old.

Path

keystringRequired
Your organization's API key. Keep the convar out of anything players can read.
playerstringRequired

Can refuse with 400, 401, 403, 404. What each means

curl "https://api.mizan.ac/v1/rust/mzn_.../76561197960276590"
Answer
{
  "steamId": "76561197960276590",
  "reason": "Banned for cheating by Example Network. This server checks the ban lists Mizan reads. To dispute it: https://mizan.ac/disputes",
  "expiryDate": 0,
  "isMute": false
}

Follow every ban change

GET/v1/bans/changesNeeds a key

Every ban the registry created, updated, ended or removed, in the order the changes were made, a page at a time, for keeping your own copy. Start without after: the answer is empty and its nextCursor is now. Then check the players you track once with POST /v1/players/batch, and from then on ask with after set to the last nextCursor, as often as you like; while hasMore is true, ask again at once. A change is never skipped and never given twice in order, though after a retry you may see one again: apply them by ban.id. A cursor is good for 29 days; an older one answers 410, and you start over.

Query

afterstring
The nextCursor of your last answer, as it came: sealed, so it can be neither read nor made. Leave it out to start now.
limitinteger
Changes per page.

Answer

data[].typestring
created: first seen in this change. ended: it now has endedAt (lifted, appealed or ended by its list). updated: anything else about it changed. removed: its list no longer has it at all: delete it from your copy. One of created, updated, ended, removed.
data[].banobject
data[].ban.idstring
The ban's id, the same in every change and in a player's record.
data[].ban.sourcestring
The list that made it (GET /v1/sources).
data[].ban.sourceNamestring or nullcan be missing
data[].ban.playerobject
data[].ban.playerNamestring or nullcan be missing
data[].ban.scopestringcan be missing
One of network, game, mode, server.
data[].ban.gamestring or nullcan be missing
data[].ban.modestring or nullcan be missing
data[].ban.reasonstring or nullcan be missing
data[].ban.categoriesarray of stringscan be missing
data[].ban.createdAtdate-time or nullcan be missing
data[].ban.expiresAtdate-time or nullcan be missing
data[].ban.endedAtdate-time or nullcan be missing
data[].ban.activebooleancan be missing
In force now: not ended, and not past expiresAt.

Can refuse with 400, 401, 403, 410, 429, 503. What each means

curl "https://api.mizan.ac/v1/bans/changes" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": [
    {
      "type": "created",
      "ban": {
        "id": "mizan:48211:external:blackwonder:118220",
        "source": "external:blackwonder",
        "sourceName": "BlackWonder.TF",
        "player": {
          "provider": "steam",
          "id": "76561197960287930"
        },
        "playerName": "someone",
        "scope": "game",
        "game": "tf2",
        "mode": null,
        "reason": "Cheating",
        "categories": [
          "CHEATING"
        ],
        "createdAt": "2026-10-29T18:01:05.000Z",
        "expiresAt": null,
        "endedAt": null,
        "active": true
      }
    },
    {
      "type": "removed",
      "ban": {
        "id": "mizan:9120:external:skial:5512",
        "source": "external:skial",
        "sourceName": "Skial",
        "player": {
          "provider": "steam",
          "id": "76561198000000011"
        }
      }
    }
  ],
  "nextCursor": "q9Xc2mT0b1Lr7YwKfA3sZp8uNvE5hG6jR4dQyB2xC0iOaWkU1tHn_eJlMgS-oVPzD7FbI",
  "hasMore": false
}

Add or update your bans

POST/v1/bansNeeds a key

Needs bans.create (a key with today's read and write access holds it) and contributor status. Contributor organizations only. Up to 100 bans, each from one of your servers; each shows on the player's public record under your organization's name.

Body

bansarray of objectsRequired
At most 100.
bans[].playerstringRequired
Any game's player: provider:id (steam, minecraft, fivem, epic, xbox, discord), or without a prefix a Steam id in any form (SteamID64, SteamID2 STEAM_0:0:11101, SteamID3 [U:1:22202], the account id 22202, a steamcommunity.com/profiles/ link), a Minecraft UUID with or without dashes, or a FiveM license: hash. With server, a bare id is read the way that server's game names players.
bans[].serverstringRequired
The server it was issued on, by the key you listed it under. It gives the ban its game and mode.
bans[].refstringRequired
Your own id for the ban. Sending it again updates the ban.
bans[].reasonstringRequired
At most 500 characters.
bans[].categoriesarray of strings
From MRDM RDM NITRP LTAP CNTAP CTAP FAILRP NLR MNLR RDA PROPBLOCK PTOX PRIORS CHEATING EXPLOIT HATE EVASION METAGAME POWERGAME MINGE SPAM ADVERT STAFF THREAT SCAM SEXUAL TRANSFER. Read from the reason when left out.
bans[].createdAtdate-time or null
ISO 8601 or epoch seconds. Now when left out.
bans[].expiresAtdate-time or null
Null or left out for a permanent ban.
bans[].playerNamestring
At most 64 characters.

Answer

data.receivedinteger
data.insertedinteger
data.updatedinteger
data.endedinteger
Always 0: end a ban with DELETE /v1/registry/bans/{ref}.
data.heldstringcan be missing
Present when some or all of this batch was not written. paused means staff paused your list; surge means it adds or ends more bans at once than your list usually does. inserted and updated count what was written. Send the batch again later: bans already stored are updated, not added twice. One of paused, surge.
data.refusedarray of stringscan be missing
Entries left as they were because another of your servers placed a ban with that player and ref, as bans[i].ref.

Can refuse with 400, 401, 403, 404, 429, 503. What each means

curl -X POST "https://api.mizan.ac/v1/bans" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"bans":[{"player":"76561197960276590","server":"darkrp-1","ref":"ban-1042","reason":"Mass RDM"}]}'
Answer
{
  "data": {
    "received": 0,
    "inserted": 0,
    "updated": 0,
    "ended": 0,
    "held": "paused",
    "refused": [
      "string"
    ]
  }
}

End one of your bans

DELETE/v1/bans/{ban}Needs a keyDeprecated

Kept for callers already using it; new code ends a ban with POST /v1/bans/{ban}/end, which also takes the ban's id and answers its final state. Needs bans.end. An unban or a successful appeal. The record keeps the ban as ended. A ref is yours per player. When more than one player has an open ban with it, name the player with player, or nothing ends and the answer is 409.

Path

banstringRequired
The ban's ref, your own id for it.

Query

playerstring
The player whose ban ends. Any game's player: provider:id (steam, minecraft, fivem, epic, xbox, discord), or without a prefix a Steam id in any form (SteamID64, SteamID2 STEAM_0:0:11101, SteamID3 [U:1:22202], the account id 22202, a steamcommunity.com/profiles/ link), a Minecraft UUID with or without dashes, or a FiveM license: hash. URL-encode SteamID3's brackets.

Answer

data.endedinteger

Can refuse with 400, 401, 403, 404, 409, 429, 503. What each means

curl -X DELETE "https://api.mizan.ac/v1/bans/ban-1042" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "ended": 0
  }
}

Steam

Steam's own record of accounts: VAC, game, community and trade bans, read for you and held per account.

Steam's record of one account

GET/v1/steam/{player}Needs a key

Every field Steam's GetPlayerBans gives for the account, without reading Mizan's registry. Mizan reads Steam for you and keeps each account's record up to six hours. Waits up to three seconds for Steam; an account Steam has not answered by then comes back as null with steamState pending (and Cache-Control: no-store) and is ready on the next ask. Live Steam calls for your players go on your community's own Steam key when it has one, else on Mizan's within a daily allowance; past it, steamState is quota_exhausted. Every player answer carries the same record as steam.

Path

playerstringRequired
A Steam id in any form: SteamID64, steam:..., STEAM_0:..., [U:1:...] (URL-encode the brackets), or a steamcommunity.com/profiles/ link.

Answer

data.playerobject
data.player.providerstring
data.player.idstring
data.steamvalue
data.steamStatestring or null
Where steam stands: ready when Mizan has Steam's answer (steam is the record, or null when Steam does not know the account); pending while Mizan is still reading it, so ask again shortly; quota_exhausted when Mizan's daily Steam allowance for your community is spent and it has no working key of its own (add one under Settings), so Steam was not asked. Reads Mizan already holds are always answered. Null when the player is not on Steam. One of ready, pending, quota_exhausted, null.

Can refuse with 400, 401, 403, 429, 503. What each means

curl "https://api.mizan.ac/v1/steam/76561197960287930" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "player": {
      "provider": "steam",
      "id": "76561197960287930"
    },
    "steam": {
      "vacBanned": true,
      "vacBans": 1,
      "gameBans": 0,
      "daysSinceLastBan": 412,
      "communityBanned": false,
      "economyBan": "none",
      "tradeBanned": false,
      "accountCreatedAt": "2014-06-02T17:12:40.000Z",
      "bans": null,
      "checkedAt": "2026-10-29T18:02:11.000Z"
    },
    "steamState": "ready"
  }
}

Organization

The organization a key belongs to, with its servers.

Who the key is for

GET/v1/meNeeds a key

The organization your key belongs to: whether it can only read records (reader) or also add bans (contributor), and its servers, each with the key you send as server in lookups and bans. A quick way to check a key works.

Answer

data.idstring
data.namestring
data.statusstring
One of reader, contributor.
data.contributeboolean
data.sourcestring
data.serversarray of objects
data.servers[].keystring
data.servers[].namestring
data.servers[].gamestring
data.servers[].modestring or null
data.servers[].addressstring or null
data.servers[].regionstring or null
data.servers[].identitystring or null

Can refuse with 400, 401, 403, 404, 429, 503. What each means

curl "https://api.mizan.ac/v1/me" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "id": "string",
    "name": "string",
    "status": "reader",
    "contribute": false,
    "source": "string",
    "servers": [
      {
        "key": "string",
        "name": "string",
        "game": "string",
        "mode": "string",
        "address": "play.example.com:27015",
        "region": "eu",
        "identity": "string"
      }
    ]
  }
}

Health

Whether the API and its databases are answering.

Check that the API and its databases answer

GET/v1/healthNo key

No key, CORS open. Each database runs one trivial query: status is pass with 200 when every one answered, and fail with 503 when one did not. A passing answer is kept for ten seconds (Cache-Control: max-age=10), so polling faster reads the same copy. Its format is draft-inadarei-api-health-check's application/health+json.

Can refuse with 429, 503. What each means

curl "https://api.mizan.ac/v1/health"
Live answer
{
  "status": "pass",
  "checks": {
    "registry-database:responseTime": [
      {
        "componentType": "datastore",
        "observedValue": 9,
        "observedUnit": "ms",
        "status": "pass",
        "time": "2026-10-31T04:50:34.214Z"
      }
    ]
  }
}
Read from the live API on 31. lokakuuta 2026 klo 04.50 UTC.

Calls the live API from your browser. No key needed.

Players

Players and every list's bans on them.

List a player's bans

GET/v1/players/{player}/bansKey optional

Every list's bans on one player, newest first, a page at a time: the record GET /v1/players/{player} carries, without its cap of 200.

No key needed: every ban here is public, and without a key the answer is cached publicly for a minute and counted against the public limits by address. With a key it needs players.view and answers Cache-Control: private.

The owner's protected accounts answer an empty list, as every registry read does.

Path

playerstringRequired
A player id in any form the game uses: SteamID64, steam:..., STEAM_0:..., [U:1:...] (URL-encoded), a Minecraft UUID.

Query

inForceboolean
true for bans in force now, false for ended or expired ones; both when left out.
sourcestring
Only these lists' bans, by key (GET /v1/sources), comma separated.
gamestring
Only bans in these games, comma separated.
limitinteger
How many per page, 1 to 100; 25 when left out. A page can hold fewer: only hasMore: false ends the list.
afterstring
The nextCursor of the page before, as it came. It carries the filters it was made with, so send the same ones; it lasts three days.

Answer

data[].objectstring
One of ban.
data[].idstring
The ban's id, the same here, in the change feed and in a player's record.
data[].sourcestring
The list that made it (GET /v1/sources).
data[].sourceNamestring or null
data[].playerobject
data[].player.providerstring
The platform the id belongs to.
data[].player.idstring
data[].playerNamestring or null
The name the list recorded, if any.
data[].scopestring
Where it applies: network, game, mode or server. A set that can grow.
data[].gamestring or null
data[].modestring or null
data[].reasonstring or null
The list's own words; an account id written into it reads another account.
data[].categoriesarray of strings
What it was for. A set that can grow.
data[].createdAtdate-time or null
data[].expiresAtdate-time or null
data[].endedAtdate-time or null
When it was lifted, appealed or ended by its list; null while it stands.
data[].activeboolean
In force now: not ended and not past expiresAt. The inForce filter reads it.

Can refuse with 400, 403, 410, 429, 503. What each means

curl "https://api.mizan.ac/v1/players/76561197960287930/bans"
Answer
{
  "object": "list",
  "data": [
    {
      "object": "ban",
      "id": "mizan:48211:external:blackwonder:118220",
      "source": "external:blackwonder",
      "sourceName": "BlackWonder.TF",
      "player": {
        "provider": "steam",
        "id": "76561197960287930"
      },
      "playerName": "someone",
      "scope": "game",
      "game": "tf2",
      "mode": null,
      "reason": "Cheating",
      "categories": [
        "CHEATING"
      ],
      "createdAt": "2026-10-29T18:01:05.000Z",
      "expiresAt": null,
      "endedAt": null,
      "active": true
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Bans

Your community's own list: read it, end bans, import a file.

List your community's bans

GET/v1/bansNeeds a key

Your community's own list, newest first, 50 a page. Needs bans.view. A key limited to some servers still reads every ban of the community: they apply on all its servers. Part of the dashboard's Bans section: while that ships locked, it answers 403 feature_not_available.

Query

inForceboolean
true for bans in force now, false for ended or expired ones; both when left out.
playerstring
Only this player's bans, by any id form.
qstring
Words in the reason or the player's name, or a ban's exact ref.
afterstring
The nextCursor of the page before, as it came. It carries the filters it was made with, so send the same ones; it lasts three days.

Answer

data[].objectstring
One of ban.
data[].idstring
The ban's id, the same here, in the change feed and in a player's record.
data[].sourcestring
The list that made it (GET /v1/sources).
data[].sourceNamestring or null
data[].playerobject
data[].player.providerstring
The platform the id belongs to.
data[].player.idstring
data[].playerNamestring or null
The name the list recorded, if any.
data[].scopestring
Where it applies: network, game, mode or server. A set that can grow.
data[].gamestring or null
data[].modestring or null
data[].reasonstring or null
The list's own words; an account id written into it reads another account.
data[].categoriesarray of strings
What it was for. A set that can grow.
data[].createdAtdate-time or null
data[].expiresAtdate-time or null
data[].endedAtdate-time or null
When it was lifted, appealed or ended by its list; null while it stands.
data[].activeboolean
In force now: not ended and not past expiresAt. The inForce filter reads it.
data[].refstring
Your own id for the ban: the ref you sent, or the one Mizan made.
data[].serverstring or null
The server of yours that placed it.

Can refuse with 400, 401, 403, 410, 429, 503. What each means

curl "https://api.mizan.ac/v1/bans" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "object": "list",
  "data": [
    {
      "object": "ban",
      "id": "mizan:48211:external:community.mizan.my-rp:mzn-tz3k1c-8f02aa31b7c4",
      "ref": "mzn-tz3k1c-8f02aa31b7c4",
      "source": "external:community.mizan.my-rp",
      "sourceName": "My RP",
      "player": {
        "provider": "steam",
        "id": "76561197960287930"
      },
      "playerName": "someone",
      "scope": "server",
      "game": "garrysmod",
      "mode": "darkrp",
      "server": "darkrp-1",
      "reason": "Aimbot",
      "categories": [
        "CHEATING"
      ],
      "createdAt": "2026-10-29T18:01:05.000Z",
      "expiresAt": null,
      "endedAt": null,
      "active": true
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Read one of your bans

GET/v1/bans/{ban}Needs a key

One ban from your community's list, open or ended. Needs bans.view. Part of the dashboard's Bans section: while that ships locked, it answers 403 feature_not_available.

Path

banstringRequired
The ban's id, or its ref (URL-encode either). A ref names the one open ban of yours that has it, or with none open the one ended ban that had it; a ref that names several answers 409: use the id.

Answer

data.objectstring
One of ban.
data.idstring
The ban's id, the same here, in the change feed and in a player's record.
data.sourcestring
The list that made it (GET /v1/sources).
data.sourceNamestring or null
data.playerobject
data.player.providerstring
The platform the id belongs to.
data.player.idstring
data.playerNamestring or null
The name the list recorded, if any.
data.scopestring
Where it applies: network, game, mode or server. A set that can grow.
data.gamestring or null
data.modestring or null
data.reasonstring or null
The list's own words; an account id written into it reads another account.
data.categoriesarray of strings
What it was for. A set that can grow.
data.createdAtdate-time or null
data.expiresAtdate-time or null
data.endedAtdate-time or null
When it was lifted, appealed or ended by its list; null while it stands.
data.activeboolean
In force now: not ended and not past expiresAt. The inForce filter reads it.
data.refstring
Your own id for the ban: the ref you sent, or the one Mizan made.
data.serverstring or null
The server of yours that placed it.

Can refuse with 400, 401, 403, 404, 409, 429, 503. What each means

curl "https://api.mizan.ac/v1/bans/mzn-tz3k1c-8f02aa31b7c4" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "object": "ban",
    "id": "mizan:48211:external:community.mizan.my-rp:mzn-tz3k1c-8f02aa31b7c4",
    "ref": "mzn-tz3k1c-8f02aa31b7c4",
    "source": "external:community.mizan.my-rp",
    "sourceName": "My RP",
    "player": {
      "provider": "steam",
      "id": "76561197960287930"
    },
    "playerName": "someone",
    "scope": "server",
    "game": "garrysmod",
    "mode": "darkrp",
    "server": "darkrp-1",
    "reason": "Aimbot",
    "categories": [
      "CHEATING"
    ],
    "createdAt": "2026-10-29T18:01:05.000Z",
    "expiresAt": null,
    "endedAt": null,
    "active": true
  }
}

End one of your bans

POST/v1/bans/{ban}/endNeeds a key

Ends the ban now (an unban, or an appeal you accepted); the record keeps it as ended. Needs bans.end and contributor status.

Safe to repeat: a ban already over answers 200 with its final state and writes nothing, so no Idempotency-Key is needed. A key limited to some servers ends only bans its servers placed (403 server_not_allowed otherwise).

If Mizan holds the change (staff paused your list, or it ends far more bans at once than your list usually does) it answers 202 with held, and nothing ended. Replaces DELETE /v1/bans/{ref}, which keeps working.

Part of the dashboard's Bans section: while that ships locked, it answers 403 feature_not_available.

Path

banstringRequired
The ban's id, or its ref (URL-encode either). A ref names the one open ban of yours that has it, or with none open the one ended ban that had it; a ref that names several answers 409: use the id.

Answer

data.objectstring
One of ban.
data.idstring
The ban's id, the same here, in the change feed and in a player's record.
data.sourcestring
The list that made it (GET /v1/sources).
data.sourceNamestring or null
data.playerobject
data.player.providerstring
The platform the id belongs to.
data.player.idstring
data.playerNamestring or null
The name the list recorded, if any.
data.scopestring
Where it applies: network, game, mode or server. A set that can grow.
data.gamestring or null
data.modestring or null
data.reasonstring or null
The list's own words; an account id written into it reads another account.
data.categoriesarray of strings
What it was for. A set that can grow.
data.createdAtdate-time or null
data.expiresAtdate-time or null
data.endedAtdate-time or null
When it was lifted, appealed or ended by its list; null while it stands.
data.activeboolean
In force now: not ended and not past expiresAt. The inForce filter reads it.
data.refstring
Your own id for the ban: the ref you sent, or the one Mizan made.
data.serverstring or null
The server of yours that placed it.

Can refuse with 400, 401, 403, 404, 409, 429, 503. What each means

curl -X POST "https://api.mizan.ac/v1/bans/mzn-tz3k1c-8f02aa31b7c4/end" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "object": "ban",
    "id": "mizan:48211:external:community.mizan.my-rp:mzn-tz3k1c-8f02aa31b7c4",
    "ref": "mzn-tz3k1c-8f02aa31b7c4",
    "source": "external:community.mizan.my-rp",
    "sourceName": "My RP",
    "player": {
      "provider": "steam",
      "id": "76561197960287930"
    },
    "playerName": "someone",
    "scope": "server",
    "game": "garrysmod",
    "mode": "darkrp",
    "server": "darkrp-1",
    "reason": "Aimbot",
    "categories": [
      "CHEATING"
    ],
    "createdAt": "2026-10-29T18:01:05.000Z",
    "expiresAt": null,
    "endedAt": "2026-10-29T19:00:00.000Z",
    "active": false
  }
}

Import a ban file

POST/v1/bans/importNeeds a key

Reads a ban list in one of the dashboard's import formats and adds or updates each ban on your list, keyed by player and the file's own id, so sending the same file twice writes nothing new and no Idempotency-Key is needed. Needs bans.import and contributor status.

With ?preview=true, nothing is written: the answer counts what the file holds and shows its first 20 bans and skipped rows.

Part of the dashboard's Bans section: while that ships locked, it answers 403 feature_not_available.

Query

previewstring
true to read the file without writing it.

Body

serverstringRequired
The server of yours the bans were placed on.
formatstringRequired
The file's format, as the dashboard's import offers them. One of json, csv, source, minecraft, table, discord.
textstringRequired
The file's contents, up to 2 MiB and 20,000 bans. At most 2097152 characters.

Answer

data.objectstring
One of ban_import.
data.previewboolean
data.bansinteger
Bans the file holds.
data.skippedinteger
Rows Mizan could not read as a ban.
data.insertedinteger or null
data.updatedinteger or null
data.samplearray of objects
The file's first 20 bans as Mizan read them.
data.sample[].playerobject
data.sample[].namestring or null
data.sample[].reasonstring or null
data.sample[].categoriesarray of strings
data.sample[].createdAtstring or null
data.sample[].expiresAtstring or null
data.sample[].refstring
data.skippedRowsarray of objects
The first 20 rows skipped, and why.
data.skippedRows[].rowinteger
data.skippedRows[].reasonstring

Can refuse with 400, 401, 403, 413, 415, 429, 503. What each means

curl -X POST "https://api.mizan.ac/v1/bans/import" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"server":"darkrp-1","format":"source","text":"banid 0 STEAM_0:1:5\nbanid 30 STEAM_0:0:7\nwriteid\n"}'
Answer
{
  "data": {
    "object": "ban_import",
    "preview": false,
    "bans": 1,
    "skipped": 0,
    "inserted": 1,
    "updated": 0,
    "sample": [
      {
        "player": {
          "provider": "steam",
          "id": "76561197960287930"
        },
        "name": null,
        "reason": "Aimbot",
        "categories": [
          "CHEATING"
        ],
        "createdAt": "2026-10-01T00:00:00.000Z",
        "expiresAt": null,
        "ref": "76561197960287930:1"
      }
    ],
    "skippedRows": []
  }
}

Joins

Joins your servers report, what Mizan and your policy decided, and the policy itself.

List recent joins

GET/v1/joinsNeeds a key

The joins your servers reported (POST /v1/joins) with what Mizan and your policy decided, and on servers that send no join checks the joins RCON saw. Newest first. Needs joins.view; a key limited to some servers reads only theirs. Part of the dashboard's Joins section: while that ships locked, it answers 403 feature_not_available.

Query

serverstring
Only this server's joins.
decisionstring
Only joins your policy allowed, flagged or refused.
tierstring
Only joins checked at this risk tier.
sincedate-time
The oldest join, RFC 3339; seven days ago when left out, at most 90 days ago.
untildate-time
The newest join, RFC 3339; now when left out.
limitinteger
How many per page, 1 to 100; 25 when left out. A page can hold fewer: only hasMore: false ends the list.
afterstring
The nextCursor of the page before, as it came. It carries the filters it was made with, so send the same ones; it lasts three days.

Answer

data[].objectstring
One of join.
data[].idstring
The join's id; r-prefixed for a join seen only over RCON.
data[].joinedAtdate-time
data[].leftAtdate-time or null
data[].serverstring
data[].tierstring or null
Mizan's risk tier when the join was checked: clean, notice, warning or high; null for a join seen only over RCON, which nothing judged.
data[].decisionstring or null
What your join policy did: allowed, flagged or refused; null for a join seen only over RCON.
data[].reasonsarray of strings
Why: own_ban, cheating, conduct, bans. A set that can grow.
data[].namestring or null
The name the player joined with.
data[].playerobject
data[].player.providerstring
data[].player.idstring
data[].player.namestring or null

Can refuse with 400, 401, 403, 410, 429, 503. What each means

curl "https://api.mizan.ac/v1/joins" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "object": "list",
  "hasMore": false,
  "nextCursor": null,
  "data": [
    {
      "object": "join",
      "id": "88213",
      "joinedAt": "2026-10-29T18:02:11.000Z",
      "leftAt": null,
      "server": "darkrp-1",
      "tier": "high",
      "decision": "flagged",
      "reasons": [
        "cheating"
      ],
      "name": "someone",
      "player": {
        "provider": "steam",
        "id": "76561197960287930",
        "name": "someone"
      }
    }
  ]
}

Report joins and leaves

POST/v1/joinsNeeds a key

Your server reports up to 100 joins and leaves at once; each join is answered with Mizan's risk tier and what your join policy does with it (GET /v1/join-policy). Needs joins.report. Mizan never refuses a join it could not check: when it is down or your community is over its limit it answers 200 with every player let in and X-Mizan-Unchecked: unavailable or rate_limited. Joins open community by community while the dashboard's Joins section ships locked; a community not yet open gets 404.

Body

serverstringRequired
eventsarray of objectsRequired
At most 100.
events[].kindstringRequired
One of join, leave.
events[].playerstringRequired
The player's id as the server's game names them.
events[].namestring or null
At most 200 characters.
events[].atvalue
When, epoch seconds or RFC 3339, within the last day; now when left out.

Can refuse with 400, 401, 403, 404. What each means

curl -X POST "https://api.mizan.ac/v1/joins" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"server":"darkrp-1","events":[{"kind":"join","player":"76561197960287930","name":"someone"}]}'
Answer
{
  "data": [
    {
      "player": {
        "provider": "steam",
        "id": "76561197960287930"
      },
      "tier": "high",
      "action": "flagged",
      "reasons": [
        "cheating"
      ],
      "own": {
        "banned": false
      }
    }
  ]
}

Read your join policy

GET/v1/join-policyNeeds a key

What your servers do with each join POST /v1/joins checks. Needs joins.view. The ETag is what PUT takes as If-Match. Part of the dashboard's Joins section: while that ships locked, it answers 403 feature_not_available.

Answer

data.objectstring
One of join_policy.
data.policyobject
What happens when a flagged player joins. cheating, ownBans: one of log, flag, refuse; conduct: log or flag; cheatingDays: how recent a cheating ban counts, 1-3650; sameGameOnly: count only cheating bans in the server's own game. The Steam rows (vacAction and the other ...Action fields, each allow, log, flag or refuse, with their numbers) act on Steam's bans and account checks; steamUnknownAction (allow or flag) applies while Steam has not answered for a player.
data.policy.cheatingstring
One of log, flag, refuse.
data.policy.cheatingDaysinteger
data.policy.sameGameOnlyboolean
data.policy.conductstring
One of log, flag.
data.policy.ownBansstring
One of log, flag, refuse.
data.policy.steamUnknownActionstring
One of allow, flag.
data.policy.vacActionstring
One of allow, log, flag, refuse.
data.policy.vacMoreinteger
data.policy.vacDaysinteger
data.policy.gameBansActionstring
One of allow, log, flag, refuse.
data.policy.gameBansMininteger
data.policy.gameBansDaysinteger
data.policy.communityBanActionstring
One of allow, log, flag, refuse.
data.policy.tradeProbationActionstring
One of allow, log, flag, refuse.
data.policy.tradeBannedActionstring
One of allow, log, flag, refuse.
data.policy.altActionstring
One of allow, log, flag, refuse.
data.policy.accountAgeActionstring
One of allow, log, flag, refuse.
data.policy.accountAgeDaysinteger
data.policy.levelActionstring
One of allow, log, flag, refuse.
data.policy.levelBelowinteger
data.policy.xpActionstring
One of allow, log, flag, refuse.
data.policy.xpBelowinteger
data.policy.serviceBadgeActionstring
One of allow, log, flag, refuse.
data.policy.avatarActionstring
One of allow, log, flag, refuse.
data.policy.setupActionstring
One of allow, log, flag, refuse.
data.policy.friendsActionstring
One of allow, log, flag, refuse.
data.policy.friendsBelowinteger
data.policy.friendAvatarsActionstring
One of allow, log, flag, refuse.
data.policy.friendAvatarsPercentinteger
data.policy.groupsActionstring
One of allow, log, flag, refuse.
data.policy.groupsBelowinteger
data.policy.bannedFriendsActionstring
One of allow, log, flag, refuse.
data.policy.bannedFriendsMininteger
data.policy.privateProfileActionstring
One of allow, log, flag, refuse.
data.policy.privateGamesActionstring
One of allow, log, flag, refuse.
data.serversobject
Each server that sets rules of its own, and how many; those servers follow their own for those rules.
data.updatedAtdate-time or null
When the policy last changed; null while it is the defaults.

Can refuse with 401, 403, 429, 503. What each means

curl "https://api.mizan.ac/v1/join-policy" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "object": "join_policy",
    "policy": {
      "cheating": "flag",
      "cheatingDays": 365,
      "sameGameOnly": false,
      "conduct": "log",
      "ownBans": "refuse",
      "steamUnknownAction": "allow",
      "vacAction": "refuse",
      "vacMore": 1,
      "vacDays": 90,
      "gameBansAction": "allow",
      "gameBansMin": 1,
      "gameBansDays": 0,
      "communityBanAction": "allow",
      "tradeProbationAction": "allow",
      "tradeBannedAction": "allow",
      "altAction": "allow",
      "accountAgeAction": "allow",
      "accountAgeDays": 30,
      "levelAction": "allow",
      "levelBelow": 3,
      "xpAction": "allow",
      "xpBelow": 300,
      "serviceBadgeAction": "allow",
      "avatarAction": "allow",
      "setupAction": "allow",
      "friendsAction": "allow",
      "friendsBelow": 1,
      "friendAvatarsAction": "allow",
      "friendAvatarsPercent": 50,
      "groupsAction": "allow",
      "groupsBelow": 1,
      "bannedFriendsAction": "allow",
      "bannedFriendsMin": 1,
      "privateProfileAction": "allow",
      "privateGamesAction": "allow"
    },
    "servers": {},
    "updatedAt": "2026-10-29T18:02:11.000Z"
  }
}

Replace your join policy

PUT/v1/join-policyNeeds a key

Replaces the community's policy with the one sent, every rule given. Needs joins.policy. Send the ETag of the policy you read as If-Match so a change made since is not overwritten: a different one answers 412 precondition_failed. Safe to repeat. The change is recorded in your audit log. Part of the dashboard's Joins section: while that ships locked, it answers 403 feature_not_available.

Query

if-matchstring
The ETag of the policy you read, or *.

Body

cheatingstringRequired
One of log, flag, refuse.
cheatingDaysintegerRequired
sameGameOnlybooleanRequired
conductstringRequired
One of log, flag.
ownBansstringRequired
One of log, flag, refuse.
steamUnknownActionstringRequired
One of allow, flag.
vacActionstringRequired
One of allow, log, flag, refuse.
vacMoreintegerRequired
vacDaysintegerRequired
gameBansActionstringRequired
One of allow, log, flag, refuse.
gameBansMinintegerRequired
gameBansDaysintegerRequired
communityBanActionstringRequired
One of allow, log, flag, refuse.
tradeProbationActionstringRequired
One of allow, log, flag, refuse.
tradeBannedActionstringRequired
One of allow, log, flag, refuse.
altActionstringRequired
One of allow, log, flag, refuse.
accountAgeActionstringRequired
One of allow, log, flag, refuse.
accountAgeDaysintegerRequired
levelActionstringRequired
One of allow, log, flag, refuse.
levelBelowintegerRequired
xpActionstringRequired
One of allow, log, flag, refuse.
xpBelowintegerRequired
serviceBadgeActionstringRequired
One of allow, log, flag, refuse.
avatarActionstringRequired
One of allow, log, flag, refuse.
setupActionstringRequired
One of allow, log, flag, refuse.
friendsActionstringRequired
One of allow, log, flag, refuse.
friendsBelowintegerRequired
friendAvatarsActionstringRequired
One of allow, log, flag, refuse.
friendAvatarsPercentintegerRequired
groupsActionstringRequired
One of allow, log, flag, refuse.
groupsBelowintegerRequired
bannedFriendsActionstringRequired
One of allow, log, flag, refuse.
bannedFriendsMinintegerRequired
privateProfileActionstringRequired
One of allow, log, flag, refuse.
privateGamesActionstringRequired
One of allow, log, flag, refuse.

Answer

data.objectstring
One of join_policy.
data.policyobject
What happens when a flagged player joins. cheating, ownBans: one of log, flag, refuse; conduct: log or flag; cheatingDays: how recent a cheating ban counts, 1-3650; sameGameOnly: count only cheating bans in the server's own game. The Steam rows (vacAction and the other ...Action fields, each allow, log, flag or refuse, with their numbers) act on Steam's bans and account checks; steamUnknownAction (allow or flag) applies while Steam has not answered for a player.
data.policy.cheatingstring
One of log, flag, refuse.
data.policy.cheatingDaysinteger
data.policy.sameGameOnlyboolean
data.policy.conductstring
One of log, flag.
data.policy.ownBansstring
One of log, flag, refuse.
data.policy.steamUnknownActionstring
One of allow, flag.
data.policy.vacActionstring
One of allow, log, flag, refuse.
data.policy.vacMoreinteger
data.policy.vacDaysinteger
data.policy.gameBansActionstring
One of allow, log, flag, refuse.
data.policy.gameBansMininteger
data.policy.gameBansDaysinteger
data.policy.communityBanActionstring
One of allow, log, flag, refuse.
data.policy.tradeProbationActionstring
One of allow, log, flag, refuse.
data.policy.tradeBannedActionstring
One of allow, log, flag, refuse.
data.policy.altActionstring
One of allow, log, flag, refuse.
data.policy.accountAgeActionstring
One of allow, log, flag, refuse.
data.policy.accountAgeDaysinteger
data.policy.levelActionstring
One of allow, log, flag, refuse.
data.policy.levelBelowinteger
data.policy.xpActionstring
One of allow, log, flag, refuse.
data.policy.xpBelowinteger
data.policy.serviceBadgeActionstring
One of allow, log, flag, refuse.
data.policy.avatarActionstring
One of allow, log, flag, refuse.
data.policy.setupActionstring
One of allow, log, flag, refuse.
data.policy.friendsActionstring
One of allow, log, flag, refuse.
data.policy.friendsBelowinteger
data.policy.friendAvatarsActionstring
One of allow, log, flag, refuse.
data.policy.friendAvatarsPercentinteger
data.policy.groupsActionstring
One of allow, log, flag, refuse.
data.policy.groupsBelowinteger
data.policy.bannedFriendsActionstring
One of allow, log, flag, refuse.
data.policy.bannedFriendsMininteger
data.policy.privateProfileActionstring
One of allow, log, flag, refuse.
data.policy.privateGamesActionstring
One of allow, log, flag, refuse.
data.serversobject
Each server that sets rules of its own, and how many; those servers follow their own for those rules.
data.updatedAtdate-time or null
When the policy last changed; null while it is the defaults.

Can refuse with 400, 401, 403, 412, 429, 503. What each means

curl -X PUT "https://api.mizan.ac/v1/join-policy" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cheating":"refuse","cheatingDays":365,"sameGameOnly":false,"conduct":"flag","ownBans":"refuse"}'
Answer
{
  "data": {
    "object": "join_policy",
    "policy": {
      "cheating": "flag",
      "cheatingDays": 365,
      "sameGameOnly": false,
      "conduct": "log",
      "ownBans": "refuse",
      "steamUnknownAction": "allow",
      "vacAction": "refuse",
      "vacMore": 1,
      "vacDays": 90,
      "gameBansAction": "allow",
      "gameBansMin": 1,
      "gameBansDays": 0,
      "communityBanAction": "allow",
      "tradeProbationAction": "allow",
      "tradeBannedAction": "allow",
      "altAction": "allow",
      "accountAgeAction": "allow",
      "accountAgeDays": 30,
      "levelAction": "allow",
      "levelBelow": 3,
      "xpAction": "allow",
      "xpBelow": 300,
      "serviceBadgeAction": "allow",
      "avatarAction": "allow",
      "setupAction": "allow",
      "friendsAction": "allow",
      "friendsBelow": 1,
      "friendAvatarsAction": "allow",
      "friendAvatarsPercent": 50,
      "groupsAction": "allow",
      "groupsBelow": 1,
      "bannedFriendsAction": "allow",
      "bannedFriendsMin": 1,
      "privateProfileAction": "allow",
      "privateGamesAction": "allow"
    },
    "servers": {},
    "updatedAt": "2026-10-29T18:02:11.000Z"
  }
}

Servers

Your servers and the settings Mizan's products read on each.

List your servers

GET/v1/serversNeeds a key

Your community's servers, as you added them on mizan.ac; a key limited to some servers sees only those. Any key of an approved community reads it. Not paged: a community has at most a few dozen.

Answer

data[].objectstring
One of server.
data[].keystring
The server's key: what every route that names a server takes.
data[].namestring
data[].gamestring
data[].modestring or null
data[].addressstring or null
Where players join.
data[].regionstring or null
data[].identitystring or null
How the server's game names players: a bare id sent with this server is read this way.

Can refuse with 401, 403, 429. What each means

curl "https://api.mizan.ac/v1/servers" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "object": "list",
  "data": [
    {
      "object": "server",
      "key": "darkrp-1",
      "name": "DarkRP 1",
      "game": "garrysmod",
      "mode": "darkrp",
      "address": "45.12.3.4:27015",
      "region": "eu",
      "identity": "steam"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Read one of your servers

GET/v1/servers/{server}Needs a key

One of your servers. Any key of an approved community reads it; a key limited to other servers gets 403 server_not_allowed.

Path

serverstringRequired
One of your servers, by its key (GET /v1/servers).

Answer

data.objectstring
One of server.
data.keystring
The server's key: what every route that names a server takes.
data.namestring
data.gamestring
data.modestring or null
data.addressstring or null
Where players join.
data.regionstring or null
data.identitystring or null
How the server's game names players: a bare id sent with this server is read this way.

Can refuse with 400, 401, 403, 404, 429. What each means

curl "https://api.mizan.ac/v1/servers/darkrp-1" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "object": "server",
    "key": "darkrp-1",
    "name": "DarkRP 1",
    "game": "garrysmod",
    "mode": "darkrp",
    "address": "45.12.3.4:27015",
    "region": "eu",
    "identity": "steam"
  }
}

Read a server's settings

GET/v1/servers/{server}/settingsNeeds a key

The settings Mizan's products read on this server (the AI anti-cheat reads them every few minutes). Any key of an approved community that may act for the server reads them. Cache-Control: private, no-cache with an ETag: send it back as If-None-Match and an unchanged answer is 304, and as If-Match on PUT.

Path

serverstringRequired
One of your servers, by its key (GET /v1/servers).

Answer

data.serverstring
data.settingsobject
Every setting's value, defaults filled in. A set that grows as products are added. - ai_anticheat.enabled: Run the AI anti-cheat. Off, nothing is checked on this server. true or false; default false. - ai_anticheat.min_confidence: Minimum confidence. A detection below this is recorded, never acted on. an integer 50-100; default 90. - ai_anticheat.violations: Violations before acting. How many detections inside the window it takes to act. an integer 1-100; default 3. - ai_anticheat.window_minutes: Violation window. Detections older than this stop counting. an integer 1-10080; default 60. - ai_anticheat.action: Action. What Mizan tells the server to do once the count is reached. one of log, flag, kick, ban; default flag. - ai_anticheat.report_only: Report only. Actions are recorded but not carried out, so you can watch before trusting it. true or false; default true.
data.updatedAtdate-time or null
When a setting last changed; null while all are defaults.

Can refuse with 401, 403, 404, 429, 503. What each means

curl "https://api.mizan.ac/v1/servers/darkrp-1/settings" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "server": "darkrp-1",
    "updatedAt": "2026-10-29T18:02:11.000Z",
    "settings": {
      "ai_anticheat.enabled": false,
      "ai_anticheat.min_confidence": 90,
      "ai_anticheat.violations": 3,
      "ai_anticheat.window_minutes": 60,
      "ai_anticheat.action": "flag",
      "ai_anticheat.report_only": true
    }
  }
}

Replace a server's settings

PUT/v1/servers/{server}/settingsNeeds a key

Replaces the server's settings with the ones sent; any left out return to their defaults, so read them first and send the ETag you read as If-Match: a change made since answers 412 precondition_failed. Needs servers.settings, and a key made on mizan.ac. Safe to repeat. Known settings: ai_anticheat.enabled, ai_anticheat.min_confidence, ai_anticheat.violations, ai_anticheat.window_minutes, ai_anticheat.action, ai_anticheat.report_only.

Path

serverstringRequired
One of your servers, by its key (GET /v1/servers).

Query

if-matchstring
The settings' ETag, or *.

Body

settingsobjectRequired
The server's settings, replacing what is stored: a setting left out returns to its default. Unknown keys and values outside a setting's range are refused.

Answer

data.serverstring
data.settingsobject
Every setting's value, defaults filled in. A set that grows as products are added. - ai_anticheat.enabled: Run the AI anti-cheat. Off, nothing is checked on this server. true or false; default false. - ai_anticheat.min_confidence: Minimum confidence. A detection below this is recorded, never acted on. an integer 50-100; default 90. - ai_anticheat.violations: Violations before acting. How many detections inside the window it takes to act. an integer 1-100; default 3. - ai_anticheat.window_minutes: Violation window. Detections older than this stop counting. an integer 1-10080; default 60. - ai_anticheat.action: Action. What Mizan tells the server to do once the count is reached. one of log, flag, kick, ban; default flag. - ai_anticheat.report_only: Report only. Actions are recorded but not carried out, so you can watch before trusting it. true or false; default true.
data.updatedAtdate-time or null
When a setting last changed; null while all are defaults.

Can refuse with 400, 401, 403, 404, 412, 429, 503. What each means

curl -X PUT "https://api.mizan.ac/v1/servers/darkrp-1/settings" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"ai_anticheat.enabled":true,"ai_anticheat.min_confidence":95,"ai_anticheat.action":"kick"}}'
Answer
{
  "data": {
    "server": "darkrp-1",
    "updatedAt": "2026-10-29T18:02:11.000Z",
    "settings": {
      "ai_anticheat.enabled": true,
      "ai_anticheat.min_confidence": 95,
      "ai_anticheat.violations": 3,
      "ai_anticheat.window_minutes": 60,
      "ai_anticheat.action": "kick",
      "ai_anticheat.report_only": true
    }
  }
}

Console

A server's RCON console: who is on, what happens, and commands.

Read a server's console

GET/v1/servers/{server}/consoleNeeds a key

RCON's state on the server, who is on, and its recent events: chat, kills, joins, commands and console lines. Needs console.view; players' addresses need players.view_ip. Cache-Control: private, no-cache: poll with after and If-None-Match. Part of the dashboard's RCON section: while that ships locked, it answers 403 feature_not_available. Needs a key made on mizan.ac.

Path

serverstringRequired
One of your servers, by its key (GET /v1/servers).

Query

afterstring
An event's id: only events after it.

Answer

data.objectstring
One of console.
data.serverstring
data.configuredboolean
False until RCON is set up for the server on mizan.ac; the lists are empty then.
data.statestring
pending, connected, auth_failed, unreachable or paused.
data.stateAtdate-time or null
data.errorstring or null
data.playersarray of objects
Who is on the server now.
data.players[].playerstring
data.players[].namestring or null
data.players[].joinedAtdate-time or null
data.players[].pinginteger or null
data.players[].addressstring or null
Only for a key holding players.view_ip; null otherwise.
data.eventsarray of objects
Without after, the latest 200, newest first. With it, up to 200 after that event, oldest first, so polling carries on without a gap.
data.events[].idstring
The event's id; send it as after to read only newer events.
data.events[].atdate-time
data.events[].kindstring
What happened: join, leave, chat, teamChat, kill, command, state, map, rename, admin, console. A set that can grow.
data.events[].playerstring or null
The player's id as the game names them.
data.events[].namestring or null
data.events[].bodystring or null
A chat line, a console line, a command.
data.events[].detailobject or null
What the kind adds: a command's output, a kill's weapon, a map. Never an address.
data.events[].actorstring or null
Who ran a command: a member's name, or apikey:<id> for a key.

Can refuse with 400, 401, 403, 404, 429, 503. What each means

curl "https://api.mizan.ac/v1/servers/darkrp-1/console" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "object": "console",
    "server": "darkrp-1",
    "configured": true,
    "state": "connected",
    "stateAt": "2026-10-29T18:00:00.000Z",
    "error": null,
    "players": [
      {
        "player": "76561197960287930",
        "name": "someone",
        "joinedAt": "2026-10-29T17:40:00.000Z",
        "ping": 48,
        "address": null
      }
    ],
    "events": [
      {
        "id": "MTc5ODAwMDAwMDo0Mg",
        "at": "2026-10-29T18:02:11.000Z",
        "kind": "chat",
        "player": "76561197960287930",
        "name": "someone",
        "body": "hello",
        "detail": null,
        "actor": null
      }
    ]
  }
}

Run a command on a server

POST/v1/servers/{server}/console/commandsNeeds a key

Queues the command for the server's RCON connection and waits up to six seconds for its answer. It runs as the server console, with full rights.

Needs console.run and, until roles' command rules exist, console.run_any: with console.run alone every command is refused.

Send an Idempotency-Key (a V4 UUID) so a retry after a timeout answers the first run instead of running the command twice; a retry while the first still runs is 409, and the same key with another command is 422. The command is recorded in your audit log.

Part of the dashboard's RCON section: while that ships locked, it answers 403 feature_not_available. Needs a key made on mizan.ac.

Path

serverstringRequired
One of your servers, by its key (GET /v1/servers).

Query

idempotency-keystring
Your id for this request, 1-255 printable characters.

Body

commandstringRequired
One line, up to 512 characters. At most 512 characters.

Answer

data.objectstring
One of command.
data.idstring
data.commandstring
data.statestring
done with its output; failed; expired (the server did not pick it up in time); running (no answer within six seconds: it may still run, so watch the console).
data.outputstring or null

Can refuse with 400, 401, 403, 404, 409, 422, 429, 503. What each means

curl -X POST "https://api.mizan.ac/v1/servers/darkrp-1/console/commands" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command":"status"}'
Answer
{
  "data": {
    "object": "command",
    "id": "9012",
    "command": "status",
    "state": "done",
    "output": "hostname: My RP\nmap: rp_downtown"
  }
}

Appeals

Appeals against your bans: read, reply, decide, and links to their evidence.

List appeals

GET/v1/appealsNeeds a key

Appeals against your community's bans in one status, newest first. Needs appeals.view. Part of the dashboard's Appeals section: while that ships locked, it answers 403 feature_not_available. Needs a key made on mizan.ac.

Query

statusstring
open when left out.
limitinteger
How many per page, 1 to 100; 25 when left out. A page can hold fewer: only hasMore: false ends the list.
afterstring
The nextCursor of the page before, as it came. It carries the filters it was made with, so send the same ones; it lasts three days.

Answer

data[].objectstring
One of appeal.
data[].idstring
data[].statusstring
open, accepted, rejected or withdrawn.
data[].createdAtdate-time
data[].updatedAtdate-time
data[].banRefstring or null
The ref of the ban appealed, or null for an appeal to the community in general.
data[].excerptstring
The appeal's first 240 characters.
data[].repliesinteger
data[].playerobject
data[].player.providerstring
The platform the id belongs to.
data[].player.idstring
data[].player.namestring or null

Can refuse with 400, 401, 403, 404, 410, 429, 503. What each means

curl "https://api.mizan.ac/v1/appeals" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "object": "list",
  "hasMore": false,
  "nextCursor": null,
  "data": [
    {
      "object": "appeal",
      "id": "1042",
      "status": "open",
      "createdAt": "2026-10-28T12:00:00.000Z",
      "updatedAt": "2026-10-28T12:00:00.000Z",
      "banRef": "mzn-tz3k1c-8f02aa31b7c4",
      "excerpt": "I was not cheating; that was a teammate on my account.",
      "replies": 0,
      "player": {
        "provider": "steam",
        "id": "76561197960287930",
        "name": "someone"
      }
    }
  ]
}

Read an appeal

GET/v1/appeals/{appeal}Needs a key

One appeal with its whole thread and the ban it names. Needs appeals.view. Part of the dashboard's Appeals section: while that ships locked, it answers 403 feature_not_available. Needs a key made on mizan.ac.

Path

appealstringRequired

Answer

data.objectstring
One of appeal.
data.idstring
data.statusstring
data.createdAtdate-time
data.updatedAtdate-time
data.decidedAtdate-time or null
data.banRefstring or null
data.bodystring
data.playerobject
data.player.providerstring
The platform the id belongs to.
data.player.idstring
data.player.namestring or null
data.appellantstring
The appellant's name on mizan.ac.
data.decidedBystring or null
Who decided: a member's name, or apikey:<id>.
data.messagesarray of objects
data.messages[].idstring
data.messages[].atdate-time
data.messages[].staffboolean
data.messages[].authorstring
data.messages[].bodystring
data.banobject or null
data.ban.refstring
data.ban.reasonstring or null
data.ban.serverstring or null
data.ban.createdAtdate-time or null
data.ban.expiresAtdate-time or null
data.ban.endedAtdate-time or null
data.ban.activeboolean

Can refuse with 400, 401, 403, 404, 429, 503. What each means

curl "https://api.mizan.ac/v1/appeals/1042" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "object": "appeal",
    "id": "1042",
    "status": "open",
    "createdAt": "2026-10-28T12:00:00.000Z",
    "updatedAt": "2026-10-28T12:00:00.000Z",
    "decidedAt": null,
    "banRef": "mzn-tz3k1c-8f02aa31b7c4",
    "body": "I was not cheating; that was a teammate on my account.",
    "player": {
      "provider": "steam",
      "id": "76561197960287930",
      "name": "someone"
    },
    "appellant": "someone",
    "decidedBy": null,
    "messages": [],
    "ban": {
      "ref": "mzn-tz3k1c-8f02aa31b7c4",
      "reason": "Aimbot",
      "server": "darkrp-1",
      "createdAt": "2026-10-27T20:00:00.000Z",
      "expiresAt": null,
      "endedAt": null,
      "active": true
    }
  }
}

Reply to an appeal

POST/v1/appeals/{appeal}/repliesNeeds a key

Adds your community's reply to an open appeal; the appellant sees it signed by the community. Needs appeals.decide. Send an Idempotency-Key so a retry does not post the reply twice. Part of the dashboard's Appeals section: while that ships locked, it answers 403 feature_not_available. Needs a key made on mizan.ac.

Path

appealstringRequired

Query

idempotency-keystring

Body

bodystringRequired
At most 4000 characters.

Answer

data.objectstring
One of appeal.
data.idstring
data.statusstring
data.createdAtdate-time
data.updatedAtdate-time
data.decidedAtdate-time or null
data.banRefstring or null
data.bodystring
data.playerobject
data.player.providerstring
The platform the id belongs to.
data.player.idstring
data.player.namestring or null
data.appellantstring
The appellant's name on mizan.ac.
data.decidedBystring or null
Who decided: a member's name, or apikey:<id>.
data.messagesarray of objects
data.messages[].idstring
data.messages[].atdate-time
data.messages[].staffboolean
data.messages[].authorstring
data.messages[].bodystring
data.banobject or null
data.ban.refstring
data.ban.reasonstring or null
data.ban.serverstring or null
data.ban.createdAtdate-time or null
data.ban.expiresAtdate-time or null
data.ban.endedAtdate-time or null
data.ban.activeboolean

Can refuse with 400, 401, 403, 404, 409, 422, 429, 503. What each means

curl -X POST "https://api.mizan.ac/v1/appeals/1042/replies" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body":"We looked at the demo again. Can you tell us which round?"}'
Answer
{
  "data": {
    "object": "appeal",
    "id": "1042",
    "status": "open",
    "createdAt": "2026-10-28T12:00:00.000Z",
    "updatedAt": "2026-10-28T12:00:00.000Z",
    "decidedAt": null,
    "banRef": "mzn-tz3k1c-8f02aa31b7c4",
    "body": "I was not cheating; that was a teammate on my account.",
    "player": {
      "provider": "steam",
      "id": "76561197960287930",
      "name": "someone"
    },
    "appellant": "someone",
    "decidedBy": null,
    "messages": [
      {
        "id": "5531",
        "at": "2026-10-29T18:02:11.000Z",
        "staff": true,
        "author": "Staff",
        "body": "We looked at the demo again. Can you tell us which round?"
      }
    ],
    "ban": {
      "ref": "mzn-tz3k1c-8f02aa31b7c4",
      "reason": "Aimbot",
      "server": "darkrp-1",
      "createdAt": "2026-10-27T20:00:00.000Z",
      "expiresAt": null,
      "endedAt": null,
      "active": true
    }
  }
}

Accept or reject an appeal

POST/v1/appeals/{appeal}/decisionNeeds a key

Decides an open appeal. Accepting ends the ban it names (with contributor status). Rejecting needs a reply in body, which the appellant reads. Needs appeals.decide. Safe to repeat: sending the decision the appeal already has answers it as it is; the other decision is 409. Part of the dashboard's Appeals section: while that ships locked, it answers 403 feature_not_available. Needs a key made on mizan.ac.

Path

appealstringRequired

Body

decisionstringRequired
One of accept, reject.
bodystring or null
A reply posted with the decision; required to reject. At most 4000 characters.

Answer

data.objectstring
One of appeal.
data.idstring
data.statusstring
data.createdAtdate-time
data.updatedAtdate-time
data.decidedAtdate-time or null
data.banRefstring or null
data.bodystring
data.playerobject
data.player.providerstring
The platform the id belongs to.
data.player.idstring
data.player.namestring or null
data.appellantstring
The appellant's name on mizan.ac.
data.decidedBystring or null
Who decided: a member's name, or apikey:<id>.
data.messagesarray of objects
data.messages[].idstring
data.messages[].atdate-time
data.messages[].staffboolean
data.messages[].authorstring
data.messages[].bodystring
data.banobject or null
data.ban.refstring
data.ban.reasonstring or null
data.ban.serverstring or null
data.ban.createdAtdate-time or null
data.ban.expiresAtdate-time or null
data.ban.endedAtdate-time or null
data.ban.activeboolean

Can refuse with 400, 401, 403, 404, 409, 429, 503. What each means

curl -X POST "https://api.mizan.ac/v1/appeals/1042/decision" \
  -H "X-API-Key: $MIZAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"decision":"accept","body":"Unbanned: the demo shows a teammate."}'
Answer
{
  "data": {
    "object": "appeal",
    "id": "1042",
    "status": "accepted",
    "createdAt": "2026-10-28T12:00:00.000Z",
    "updatedAt": "2026-10-28T12:00:00.000Z",
    "decidedAt": "2026-10-29T18:02:11.000Z",
    "banRef": "mzn-tz3k1c-8f02aa31b7c4",
    "body": "I was not cheating; that was a teammate on my account.",
    "player": {
      "provider": "steam",
      "id": "76561197960287930",
      "name": "someone"
    },
    "appellant": "someone",
    "decidedBy": "apikey:key_9f2c",
    "messages": [],
    "ban": {
      "ref": "mzn-tz3k1c-8f02aa31b7c4",
      "reason": "Aimbot",
      "server": "darkrp-1",
      "createdAt": "2026-10-27T20:00:00.000Z",
      "expiresAt": null,
      "endedAt": "2026-10-29T18:02:11.000Z",
      "active": false
    }
  }
}

List an appeal's evidence

GET/v1/appeals/{appeal}/evidenceNeeds a key

The files on an appeal, the appellant's and your staff's, newest first; never their contents: ask GET /v1/evidence/{evidence} for a link. Needs appeals.evidence. Part of the dashboard's Appeals section: while that ships locked, it answers 403 feature_not_available. Needs a key made on mizan.ac.

Path

appealstringRequired

Answer

data[].objectstring
One of evidence.
data[].idstring
data[].namestring
data[].sizeinteger
data[].contentTypestring
data[].sha256string
data[].createdAtdate-time
data[].banRefstring or null
data[].appealstring or null
data[].byAppellantboolean

Can refuse with 400, 401, 403, 404, 429, 503. What each means

curl "https://api.mizan.ac/v1/appeals/1042/evidence" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "object": "list",
  "hasMore": false,
  "nextCursor": null,
  "data": [
    {
      "object": "evidence",
      "id": "77",
      "name": "demo.dem",
      "size": 5242880,
      "contentType": "application/octet-stream",
      "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "createdAt": "2026-10-28T12:05:00.000Z",
      "banRef": "mzn-tz3k1c-8f02aa31b7c4",
      "appeal": "1042",
      "byAppellant": true
    }
  ]
}

Get a link to a file

GET/v1/evidence/{evidence}Needs a key

A signed link to one of your community's files that works for seven days without signing in. Each link made is recorded in your audit log. Needs appeals.evidence. Part of the dashboard's Appeals section: while that ships locked, it answers 403 feature_not_available. Needs a key made on mizan.ac.

Path

evidencestringRequired

Answer

data.objectstring
One of evidence_link.
data.idstring
data.urlstring
data.expiresAtdate-time

Can refuse with 400, 401, 403, 404, 429, 503. What each means

curl "https://api.mizan.ac/v1/evidence/77" \
  -H "X-API-Key: $MIZAN_API_KEY"
Answer
{
  "data": {
    "object": "evidence_link",
    "id": "77",
    "url": "https://mizan.ac/evidence/12.77.1793900000.abc",
    "expiresAt": "2026-11-05T18:00:00.000Z"
  }
}