Destructive Actions
What counts as destructive, the sentinel template for each operation, and how to open a re-auth window.
What is this?
Eight operations (strikes, bans, long mutes, mass purges, RCON commands, and their inverses) are gated by two extra checks beyond capabilities. The token needs an open 15-minute re-auth window, and the request body needs a_confirmationfield that matches a deterministic sentinel template built from the rest of the body.
Why you might want it
This is the page to read before the first destructive call from any new integration. It explains the extra step (so your script doesn't fail mysteriously), lists every sentinel template (so you can construct them in code), and documents the prompt-injection defense (so you know why an MCP client can never forge one).
What this is for
Successfully calling a destructive endpoint (strike, ban, long mute, mass purge, RCON) from a token. Two safeguards run in series: an open 15-minute re-auth window on the token, and a deterministic _confirmation sentinel in the request body.
Before you start
- ·Your guild is on the Pro tier and you hold a valid token (see Authentication and REST Quickstart).
- ·The token's capability snapshot includes the capability for the operation (e.g.
strikes.writeforPOST /strikes). - ·You know which endpoint counts as destructive: the eight rows listed below.
POST /mutesis conditional onduration_minutes > 1440; everything else is always destructive. - ·You can reach the dashboard as the token owner, or you can drive the re-auth window via the API with the user session cookie.
5-minute setup
- 1Build the sentinel string from the request body using the template for your operation (uppercase, single-spaced, placeholders filled).
- 2Try the call first. Expect
403 RE_AUTH_REQUIREDwith areauth_urlindetails. This is the gate doing its job, not a bug. - 3Open a window: from the dashboard click Approve next destructive action on the Re-auth tab, or
POST /api/api-tokens/{id}/reauth-windowwith the session cookie. - 4Retry the destructive call with the same body and sentinel. The window is bound to the token and covers every destructive call on it for 15 minutes.
- 5Confirm 201 / 200 success and an audit row tagged with the token name.
Common failure modes
- 400 INVALID_CONFIRMATION even after opening a windowThe sentinel doesn't match the template. The error response includes
details.expected_concrete— diff your sent value against it. Common cause: lowercase tokens or extra whitespace. The server uppercases and collapses spaces but won't fix misspellings. - 403 RE_AUTH_REQUIRED on the second destructive call inside a 15-minute windowYou opened the window for a different token. The window is per-token, not per-account. Open the window for the exact token that's sending the call (the URL takes the token id, not the user id).
- Script blindly retries past 423 / 403 and gets rate-limitedTreat
RE_AUTH_REQUIREDas a stop condition, not a transient error. Surface thereauth_urlto the operator, wait for them to open the window, then retry. Don't hammer the endpoint in a loop.
What Is a Destructive Action?
A destructive action mutates guild state in ways that are hard or impossible to undo. Banning a user, removing a strike, mass-purging a channel, running an arbitrary RCON command — these all leave a footprint that cannot be reversed by a follow-up call.
The public token surface guards these calls with two safeguards working in series. First, the token must have an open re-auth window for the destructive operation, which the dashboard owner approves explicitly and which expires after 15 minutes. Second, the request body must include a _confirmation field whose value matches the operation's sentinel template verbatim, with the placeholders substituted from the rest of the request body.
400 INVALID_CONFIRMATION. The correct sentinel without an open window returns 403 RE_AUTH_REQUIRED.What Counts as Destructive?
The following eight operations are destructive on the v1 surface. The trigger column notes when the destructive path applies; operations marked Always are destructive on every call, while POST /mutes is tier-gated by duration.
| Operation | Trigger | Sentinel template |
|---|---|---|
POST /strikes | Always | ADD STRIKE TO USER {user_id} IN GUILD {guildId} SEVERITY {MINOR|MAJOR} |
DELETE /strikes/{strikeId} | Always | REMOVE STRIKE {strikeId} IN GUILD {guildId} |
POST /bans | Always | BAN USER {user_id} IN GUILD {guildId} {PERMANENT|DURATION N} |
DELETE /bans/{userId} | Always | UNBAN USER {userId} IN GUILD {guildId} |
POST /mutes | duration_minutes > 1440 (>24h) | MUTE USER {user_id} IN GUILD {guildId} DURATION {N} |
DELETE /mutes/{userId} | Always | LIFT MUTE FROM USER {userId} IN GUILD {guildId} |
POST /mass-purge | Always | PURGE {count} MESSAGES IN CHANNEL {channel_id} IN GUILD {guildId} |
POST /servers/{serverId}/rcon/run | Always | RUN RCON ON SERVER {serverId} IN GUILD {guildId} |
The runtime-tier carve-out for POST /mutes is the only conditional on the list. Mutes of 24 hours or fewer are reversible and need no sentinel; mutes longer than 24 hours are destructive and require both the re-auth window and the sentinel. Every other operation on the table is destructive on every call.
Sentinel Construction Rules
The server validates the sentinel verbatim after a small normalization pass. The rules:
- All tokens are UPPERCASE. The server uppercases the input before comparing, so lowercase tokens are tolerated, but the documented form is uppercase.
- Single-space separators. The server collapses runs of whitespace to a single space before comparing.
- Placeholders inside
{ }are substituted from the request body.{guildId}comes from the URL path;{user_id},{strikeId},{count}, and similar come from the JSON body. - Choice placeholders like
{MINOR|MAJOR}mean exactly one of the listed literals. Substitute the one that matches the request. - The
_confirmationfield is required on the request body for destructive calls. Send it as a top-level string.
On mismatch the server returns 400 INVALID_CONFIRMATION with both the expected template format and the expected concrete string for the current request. That makes the error self-correcting — the client can compare what it sent against details.expected_concrete and retry.
{
"error": {
"code": "INVALID_CONFIRMATION",
"message": "_confirmation does not match the expected sentinel.",
"details": {
"expected_format": "ADD STRIKE TO USER {user_id} IN GUILD {guildId} SEVERITY {MINOR|MAJOR}",
"expected_concrete": "ADD STRIKE TO USER 123456789012345678 IN GUILD 987654321098765432 SEVERITY MINOR"
}
}
}Opening a Re-auth Window
A re-auth window is a 15-minute grant that authorizes destructive calls from a specific token. The window is tied to the token and the guild, and it expires automatically. There are two ways to open one.
From the dashboard. Open the Settings Panel from your sidebar, find the Developer Mode card and toggle it on (owner-only, per-browser, one-time). API & MCP now appears in your sidebar — open it and switch to the Re-auth tab. Next to the active-tokens list you will see an Approve next destructive action button. Click it to open a 15-minute window for that token. The Re-auth tab also shows any currently open window with a countdown so you can see at a glance whether the next destructive call will succeed.
From the API. Fully scripted workflows can open the window without a dashboard round-trip. POST /api/api-tokens/{id}/reauth-window with the user's session cookie returns the new window. This is useful when the same operator is driving both the dashboard session and the script.
POST /api/api-tokens/01HZTOK01TOKEN01TOKEN01TOK/reauth-window
Cookie: arkanis_session=...
200 OK
{
"open": true,
"window": {
"window_id": "01HZRA01WIN01WIN01WIN01WIN",
"expires_at": "2026-05-12T22:15:00.000Z"
}
}Example: Hitting RE_AUTH_REQUIRED and Recovering
Most clients will encounter the destructive guard the first time they try to issue a strike or ban from a fresh token. The recovery flow has three steps.
1. The destructive call returns 403. The token is valid and has the required capability, but no re-auth window is open. The response includes a reauth_url that points at the dashboard tab.
POST /api/public/v1/guilds/987654321098765432/strikes
Authorization: Bearer arkpat_...
Content-Type: application/json
{
"user_id": "123456789012345678",
"severity": "MINOR",
"reason": "Spam in #general",
"_confirmation": "ADD STRIKE TO USER 123456789012345678 IN GUILD 987654321098765432 SEVERITY MINOR"
}
403 Forbidden
{
"error": {
"code": "RE_AUTH_REQUIRED",
"message": "Destructive action requires an open re-auth window.",
"details": {
"reauth_url": "https://arkanis.gg/dashboard/987654321098765432/panels/settings/api-mcp?tab=reauth"
}
}
}2. Open the window. Either click the dashboard CTA or POST to the re-auth endpoint as shown above. Both result in a 15-minute window scoped to this token.
3. Retry the destructive call. The same request body, sentinel and all, will now succeed.
POST /api/public/v1/guilds/987654321098765432/strikes
Authorization: Bearer arkpat_...
Content-Type: application/json
{
"user_id": "123456789012345678",
"severity": "MINOR",
"reason": "Spam in #general",
"_confirmation": "ADD STRIKE TO USER 123456789012345678 IN GUILD 987654321098765432 SEVERITY MINOR"
}
201 Created
{
"strike": {
"id": "01HZSTRIKE01STRIKE01STRIKE0",
"user_id": "123456789012345678",
"severity": "MINOR",
"issued_at": "2026-05-12T22:01:30.000Z"
}
}The window remains open for the remainder of its 15-minute life, so a script that needs to issue several destructive operations in sequence only needs to open one window. After it expires the next destructive call returns RE_AUTH_REQUIRED again.
MCP Tools and Sentinels
When destructive operations are invoked through the MCP server, the local arkanis-mcp process constructs the sentinel from the validated tool arguments before sending the REST call. The natural-language client — Claude Desktop, Claude Code, or another MCP client — never has to author the sentinel from prose.
This is the prompt-injection defense. A user could ask the model to issue a ban, but the model has no path to forge a sentinel; the deterministic local code path holds that responsibility. The model can only invoke the tool with structured arguments, and the local construction guarantees the sentinel matches the arguments exactly.
Where to Next
- Rate Limits and Errors covers the full error envelope and every code a destructive caller should handle.
- Authentication covers token rotation and revocation, which are relevant when a destructive-capable token is compromised.
- REST Quickstart walks through the full recovery flow with copy-pasteable curl commands.