API Integration

Other plugins integrate against the small, versioned FlagForgeApi interface (flagforge-api module), obtained via Bukkit's services manager:

FlagForgeApi api = Bukkit.getServicesManager().load(FlagForgeApi.class);
if (api == null) {
    return; // FlagForge isn't installed/enabled
}

// Declare a plugin-granted reward before mutating inventory, so reconciliation doesn't flag it
// as an unexplained gain.
TransactionSpec spec = new TransactionSpec(
    "ECONOMY_REWARD", player.getUniqueId(), TransactionSpec.Direction.CREDIT,
    "minecraft:diamond", 100, Duration.ofSeconds(30));
TransactionHandle handle = api.beginItemTransaction(this, spec);
giveDiamonds(player, 100);
api.completeItemTransaction(handle);

// Grant a temporary, scoped exemption (e.g. inside your own arena/minigame plugin).
ExemptionSpec exemption = new ExemptionSpec(
    ExemptionSpec.ScopeType.CATEGORY, "COMBAT", "PvP arena zone", Duration.ofMinutes(10));
ExemptionHandle exemptionHandle = api.exempt(this, player.getUniqueId(), exemption);
// api.revoke(exemptionHandle); once the arena match ends, to end it early.

// Submit a finding from your own detection logic into FlagForge's pipeline/alerting.
api.submitFinding(this, new ExternalFinding(
    "myplugin.suspicious_teleport", Category.MOVEMENT,
    EvidenceClass.HEURISTIC, player.getUniqueId(), 5, 0.6,
    "Teleported across dimensions with no known API cause", Map.of()));

// Read a player's current aggregated risk (available once at least one finding has fired them
// through the policy engine this session).
Optional<PlayerRiskView> risk = api.risk(player.getUniqueId());

// Authorize your own plugin's items to exceed a vanilla limit, so inventory.illegal_item.a /
// inventory.illegal_stack.a never flag them - e.g. a custom enchant plugin's own Efficiency 10
// pickaxes. Server-wide and permanent (not player- or time-scoped) until revoked or this plugin
// disables, at which point every authorization it issued is revoked automatically.
ItemAuthorizationSpec authorization = new ItemAuthorizationSpec(
    "minecraft:diamond_pickaxe", ItemAuthorizationKind.ENCHANTMENT_LEVEL,
    "minecraft:efficiency", 10, "MyEnchantPlugin's crate-exclusive pickaxes");
ItemAuthorizationHandle authorizationHandle = api.authorizeItem(this, authorization);
// api.revokeItemAuthorization(authorizationHandle); if you stop handing these out.

Every handle validates the calling plugin's ownership (a handle from one plugin cannot be completed/revoked by another) and expires — an abandoned or expired handle is ignored, never silently trusted. ItemAuthorizationHandle is the one exception to expiry: it stays active until explicitly revoked or the issuing plugin disables. See the Javadoc on each flagforge-api class for full method contracts.

Worked example: a shop-gated build tool ("printer" mode)

A common pattern: a custom Creative-like mode where players place blocks rapidly through a plugin-controlled tool (charged per block via a shop, no commands allowed). The block-placement rate this produces is exactly what world.fastplace.a/world.nuker.a/world.impossible_action.a exist to catch for an ordinary player — but here it's expected and legitimate, since every placement is already gated by your own shop logic.

Don't work around this by intercepting or cancelling packets before FlagForge sees them - that fights the anti-cheat instead of telling it what's actually going on, and breaks the moment FlagForge's internals change. Do grant a scoped exemption for exactly as long as the player is in that mode:

// When a player enters your build tool:
ExemptionSpec printerMode = new ExemptionSpec(
    ExemptionSpec.ScopeType.CATEGORY, "WORLD",
    "Shop-gated build tool - placement rate is expected here", Duration.ofHours(2));
ExemptionHandle handle = api.exempt(this, player.getUniqueId(), printerMode);
activeExemptions.put(player.getUniqueId(), handle); // track it yourself, keyed by player

// When they leave (or disconnect) - revoke immediately rather than waiting out the duration:
ExemptionHandle handle = activeExemptions.remove(player.getUniqueId());
if (handle != null) {
    api.revoke(handle);
}

CATEGORY/"WORLD" covers every world check at once (placement rate, break rate, reach, etc.) for that one player, only while the exemption is active. If you only need to exempt placement specifically and still want break/reach checks active, scope it narrower instead: ExemptionSpec.ScopeType.CHECK with "world.fastplace.a" (one exempt() call per check ID you need). The duration is a safety net, not the primary mechanism - always revoke() on exit so a disconnect or a bug in your own mode-tracking can't leave a player permanently exempted.

Events

Beyond the interface, flagforge-api also ships three plain Bukkit events other plugins can listen for instead of polling: