BlazeBans API/Working with data

Creating and revoking

Issuing punishments from your own code, revoking them, and correcting a reason. Everything here goes through the same path as a staff command, so the same events fire and the same follow-up happens.

Creating a punishment

Build a PunishmentCreateRequest and submit it.

java
import java.time.Duration;
import net.blazebans.api.punishment.PunishmentCreateRequest;
import net.blazebans.api.punishment.PunishmentType;

PunishmentCreateRequest request = new PunishmentCreateRequest(
    PunishmentType.MUTE,
    target.getName(),
    target.getUniqueId(),
    targetIpAddress,
    "MyAddon",
    null,
    Duration.ofHours(2),
    "Spam",
    "https://example.com/proof/123",
    "server",
    "survival",
    false
);

api.punishments().createPunishment(request)
    .whenComplete((created, error) -> {
        if (error != null) {
            getLogger().warning("Punishment creation failed: " + error.getMessage());
            return;
        }
        if (created.isEmpty()) {
            getLogger().info("Another plugin cancelled the punishment.");
            return;
        }
        getLogger().info("Created " + created.get().id());
    });

The twelve arguments

ArgumentTypeNotes
typePunishmentTypeRequired, never null
targetNameStringSupply whenever you know it
targetUuidUUIDnull when unknown
targetIpAddressStringRaw address for IP matching, or ""
staffNameStringWho to credit. Your addon's name is fine
staffUuidUUIDnull for automated actions
durationDurationDuration.ZERO, negative, or null means permanent
reasonStringBlank uses BlazeBans' configured default
proofUrlString"" when there is none
serverScopeString"server", "global", or "server:<id>"
serverIdStringThe server this applies to when scoped
silentbooleanSuppresses announcements

Null strings are converted to "" on the way in, so passing null for reason or proofUrl will not throw. It just means "not supplied".

Pass the IP address raw. BlazeBans applies its own hashing and privacy settings; pre-hashing it will not match anything.

Name your plugin in staffName

staffName is what appears in /history, /staffhistory, staff profiles, and the Discord post. Put your system's name there.

java
"Anti-cheat"     // good
"Chat-Filter"    // good
"CONSOLE"        // useless six months later
""               // worse

This is the API equivalent of the --punisher flag on the commands, and it matters for the same reason: an operator reviewing an appeal needs to know whether a ban came from a detector, a web panel, or a person. It also makes /staffhistory Anti-cheat and /staffrollback Anti-cheat 2h work, which is how a misbehaving detector gets reviewed and undone without touching anything a human did.

Pick one name per system and keep it stable across releases, since operators will search on it.

Set staffUuid to null for anything automated. A UUID there implies a real account acted.

If your plugin punishes on behalf of a specific staff member, such as an appeals panel, use their name and UUID instead so the record credits the person who decided.

Durations

java
Duration.ofHours(2)      // two hours
Duration.ofDays(30)      // thirty days
Duration.ZERO            // permanent
null                     // permanent

Kicks are stored as permanent records regardless of what you pass, because there is nothing for a kick to expire from.

Scope

java
// This server only
request.serverScope("server");
request.serverId("survival");

// The whole network
request.serverScope("global");
request.serverId("");

Get the local server ID from an existing record, or take it from your own config. There is no API method exposing BlazeBans' configured server.id, so if your addon needs to scope to "here" the practical route is to read it from a record you already have, or let the operator set it in your own config.

What happens after

Creating a punishment through the API is not a bare database insert. In order:

  1. BlazeBansPunishmentCreateEvent fires, and listeners can modify or cancel it.
  2. The record is stored.
  3. BlazeBansPunishmentCreatedEvent fires.
  4. Enforcement runs: the player is removed for a ban, or the mute takes effect.
  5. The victim is notified, staff are broadcast to, Discord is posted, and any template console commands run.

An empty Optional means step 1 cancelled it. Nothing was stored.

Revoking by ID

java
api.punishments()
    .revokePunishment("#ABC123", "MyAddon", "Appeal accepted")
    .thenAccept(revoked -> revoked.ifPresent(punishment ->
        getLogger().info("Revoked " + punishment.id())
    ));

The # is optional. An empty result means no punishment with that ID was found and active, or a listener cancelled the revocation.

Revoking the newest match

When you have a player rather than an ID:

java
import java.util.List;
import net.blazebans.api.punishment.PunishmentRevokeRequest;
import net.blazebans.api.punishment.PunishmentType;

PunishmentRevokeRequest request = new PunishmentRevokeRequest(
    List.of(PunishmentType.MUTE, PunishmentType.IP_MUTE),
    targetNameOrUuid,
    "MyAddon",
    "Mute lifted early"
);

api.punishments().revokeActive(request);

The type list is how you say "any kind of mute". Pass both BAN and IP_BAN to match what /unban does, or a single type to be precise.

target accepts a name or a UUID string. Staff name and reason are mutable on the request, which is what lets a BlazeBansPunishmentRevokeEvent listener adjust them before it goes through.

Editing a reason

java
api.punishments().editReason("#ABC123", "Corrected reason");

Changes the stored reason and nothing else. A blank reason is rejected with an empty result.

There is no API method for changing a duration. /editpunishment does that in game.

Revocation semantics

A revoked punishment is not deleted. It keeps its record with revokedAt, revokedBy, and revokeReason filled in, and status() reports REVOKED.

Cancelling BlazeBansPunishmentRevokedEvent does not put the punishment back. It only suppresses the follow-up output. By the time that event fires, the revocation is stored.

Preferring commands

Sometimes dispatching the console command is the better tool:

java
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
    "ban " + player.getName() + " 30d Cheating --proof " + url);

Use a command when you want a template applied by ID, a ladder step chosen automatically, or any flag the API does not model. Use the API when you want the created record back, structured error handling, or to avoid string-building user input into a command line.

If you do dispatch commands, be careful with player-supplied text. A reason containing --silent will be parsed as a flag.

A complete example

Muting a player for a fixed period when your own plugin detects something:

java
public void muteForSpam(Player target, Duration duration) {
    String ip = target.getAddress() == null
        ? ""
        : target.getAddress().getAddress().getHostAddress();

    PunishmentCreateRequest request = new PunishmentCreateRequest(
        PunishmentType.MUTE,
        target.getName(),
        target.getUniqueId(),
        ip,
        "MyAddon",
        null,
        duration,
        "Automated: chat spam",
        "",
        "server",
        this.serverId,
        false
    );

    this.blazeBans.punishments().createPunishment(request)
        .whenComplete((created, error) -> {
            if (error != null) {
                getLogger().warning("Could not mute " + target.getName() + ": " + error.getMessage());
                return;
            }
            created.ifPresent(punishment ->
                getLogger().info("Muted " + target.getName() + " as " + punishment.id())
            );
        });
}

Note that nothing here touches Bukkit from the callback. If you wanted to message the player, you would schedule back first. See Threading.