Feature request: list active penalties & remove an active penalty (public API)
TL;DR: Please consider exposing a small public API to list active penalties and revoke one by
id, using SimpleAdmin's normal cache/audit/webhook path.
Context / use case
We maintain a third-party plugin that consumes SimpleAdmin penalty data (bans, mutes, gags,
silences, warnings) to provide an in-game admin UX around SimpleAdmin penalty state. The current
public API lets us create penalties (IssuePenalty) and read individual statuses
(GetPlayerInfo, GetPlayerMuteStatus), which already works well for us. What we're missing is
the read-all and the remove side, so we can stay a clean consumer of SimpleAdmin instead of
reaching around it.
Note: the existing signatures referenced below were checked against
CS2-SimpleAdminApi/ICS2-SimpleAdminApi.cs, so this request builds on the API as it actually is,
not on assumptions.
What we'd like
- List active penalties for a player / SteamID — returning a small read-only DTO.
- Remove an active penalty by its
Id, going through SimpleAdmin's own revoke path and
returning a typed result instead of relying on console output.
To keep this small, we'd be happy with just (1) list active + (2) remove active as a first
step.
Suggested shape (open to whatever fits the codebase best)
public sealed class PenaltyRecord
{
public int Id { get; init; } // stable penalty id (not currently exposed)
public string SteamId { get; init; }
public PenaltyType PenaltyType { get; init; } // existing enum, not a string
public string? Reason { get; init; }
public string? AdminSteamId { get; init; }
public DateTime CreatedUtc { get; init; }
public DateTime? ExpiresUtc { get; init; } // null = permanent
public bool IsActive { get; init; }
}
public enum PenaltyRemoveStatus { Removed, NotFound, AlreadyInactive, NotPermitted, Error }
public sealed class PenaltyRemoveResult
{
public PenaltyRemoveStatus Status { get; init; }
public int? Id { get; init; }
public string? Message { get; init; }
}
// List – the player / steamId is the actual search target
IReadOnlyList<PenaltyRecord> GetActivePenalties(CCSPlayerController player);
Task<IReadOnlyList<PenaltyRecord>> GetActivePenaltiesAsync(SteamID steamId);
// Optional convenience overload if it better matches the public API style:
// Task<IReadOnlyList<PenaltyRecord>> GetActivePenaltiesAsync(string steamId64);
// Remove – admin-scoped; the penaltyId already identifies the record uniquely
PenaltyRemoveResult RemovePenalty(CCSPlayerController admin, int penaltyId, string? reason = null);
Task<PenaltyRemoveResult> RemovePenaltyAsync(CCSPlayerController admin, int penaltyId, string? reason = null);
A few notes on the shape, all flexible:
- The DTO uses the existing
PenaltyType enum rather than a string, to stay type-safe and avoid
typos.
- For remove, we pass the
admin controller as a separate argument (mirroring how
IssuePenalty already takes admin), since revoking needs an admin actor for permissions, audit
logging, webhooks and activity output. The penaltyId already identifies the record uniquely, so
no extra target argument is needed; reason is optional and only useful for the audit trail. If
maintainers prefer target-explicit overloads for permission or logging reasons, that shape would
also work for us.
SteamID is used above because the current API already exposes/uses that type. A string/ulong
SteamID64 overload would also be useful if that better matches the public API style.
RemovePenalty should go through the same internal path as native SimpleAdmin revoke commands,
including runtime cache updates, audit logging, webhooks, and admin activity output where
applicable. The intent is the official SimpleAdmin lifecycle, not just flipping a DB row to
inactive.
Why through the API rather than other paths
We could technically work around the gap, but neither workaround is good for an integrator:
Direct DB writes would:
- bypass SimpleAdmin's in-memory caches, leaving stale runtime state until a reload;
- skip audit logging, webhooks and admin-activity notifications that hang off the official code
path;
- couple us to the internal schema, so any SimpleAdmin migration could silently break the
integration.
Console commands (Server.ExecuteCommand, css_unban, …) would:
- run without a proper admin context, making permission checks and "who revoked this" attribution
unclear;
- surface success/failure only as log text rather than a typed return value, so we can't reliably
react to the outcome;
- leave permissions and auditing ambiguous and race-prone.
An official API keeps the cache, audit trail and schema as SimpleAdmin's responsibility and gives
integrators a stable contract.
Scope
First step we'd actually use: list active + remove active. Since RemovePenalty(id) is generic,
it already covers removing an active ban when bans are listed as PenaltyRecords — so unban isn't a
separate ask. Warning count adjustments and type-specific helper methods such as
Unban/Unmute/Ungag are optional/separate and can come later if the core is too large for one
change.
Happy to help test against our setup. Thanks for SimpleAdmin — the existing API already covers our
create path well; this would just round out the read/remove side.
Feature request: list active penalties & remove an active penalty (public API)
TL;DR: Please consider exposing a small public API to list active penalties and revoke one by
id, using SimpleAdmin's normal cache/audit/webhook path.
Context / use case
We maintain a third-party plugin that consumes SimpleAdmin penalty data (bans, mutes, gags,
silences, warnings) to provide an in-game admin UX around SimpleAdmin penalty state. The current
public API lets us create penalties (
IssuePenalty) and read individual statuses(
GetPlayerInfo,GetPlayerMuteStatus), which already works well for us. What we're missing isthe read-all and the remove side, so we can stay a clean consumer of SimpleAdmin instead of
reaching around it.
What we'd like
Id, going through SimpleAdmin's own revoke path andreturning a typed result instead of relying on console output.
To keep this small, we'd be happy with just (1) list active + (2) remove active as a first
step.
Suggested shape (open to whatever fits the codebase best)
A few notes on the shape, all flexible:
PenaltyTypeenum rather than a string, to stay type-safe and avoidtypos.
admincontroller as a separate argument (mirroring howIssuePenaltyalready takesadmin), since revoking needs an admin actor for permissions, auditlogging, webhooks and activity output. The
penaltyIdalready identifies the record uniquely, sono extra target argument is needed;
reasonis optional and only useful for the audit trail. Ifmaintainers prefer target-explicit overloads for permission or logging reasons, that shape would
also work for us.
SteamIDis used above because the current API already exposes/uses that type. Astring/ulongSteamID64 overload would also be useful if that better matches the public API style.
RemovePenaltyshould go through the same internal path as native SimpleAdmin revoke commands,including runtime cache updates, audit logging, webhooks, and admin activity output where
applicable. The intent is the official SimpleAdmin lifecycle, not just flipping a DB row to
inactive.
Why through the API rather than other paths
We could technically work around the gap, but neither workaround is good for an integrator:
Direct DB writes would:
path;
integration.
Console commands (
Server.ExecuteCommand,css_unban, …) would:unclear;
react to the outcome;
An official API keeps the cache, audit trail and schema as SimpleAdmin's responsibility and gives
integrators a stable contract.
Scope
First step we'd actually use: list active + remove active. Since
RemovePenalty(id)is generic,it already covers removing an active ban when bans are listed as
PenaltyRecords — so unban isn't aseparate ask. Warning count adjustments and type-specific helper methods such as
Unban/Unmute/Ungag are optional/separate and can come later if the core is too large for one
change.
Happy to help test against our setup. Thanks for SimpleAdmin — the existing API already covers our
create path well; this would just round out the read/remove side.