Arc anti-cheat integration

Connect Arc to your backend, receive account enforcement callbacks, validate protected players, and launch your game through Arc.

POST

Revoke player access

Arc sends this callback when a protected client loses access. Your backend decides how enforcement works: invalidate authentication, remove the player from the game, or disable protected features.

/outgoing/v1/anticheat/private/revoke
{
  "id": "accountid"
}
HeaderValuePurpose
User-AgentArc/{version}Identifies the Arc service version sending the callback.
Content-Typeapplication/jsonThe callback body is JSON.
X-Arc-AuthYour configured API keyRequired. Reject the request unless this exactly matches your Arc API key.
POST

Ban a player

Arc sends this callback when a detection results in a ban. Persist the ban against the supplied account ID and prevent the account from creating or joining protected sessions.

/outgoing/v1/anticheat/private/ban
{
  "id": "accountid"
}
HeaderValuePurpose
User-AgentArc/{version}Identifies the Arc service version sending the callback.
Content-Typeapplication/jsonThe callback body is JSON.
X-Arc-AuthYour configured API keyRequired. Reject the request unless this exactly matches your Arc API key.

Verify callback authentication

Check the authentication header before reading or acting on the account ID. Return an unauthorized response when the key is missing or incorrect.

function verifyArcCallback(request: Request) {
  const receivedKey = request.headers.get("x-arc-auth");
  const expectedKey = process.env.ARC_API_KEY;

  if (!expectedKey || receivedKey !== expectedKey) {
    return new Response("Unauthorized", { status: 401 });
  }

  return null;
}

export async function handleRevoke(request: Request) {
  const rejected = verifyArcCallback(request);
  if (rejected) return rejected;

  const { id } = await request.json() as { id: string };
  await revokeGameAccess(id);
  return new Response(null, { status: 204 });
}

Check Arc status

Forward the player's Arc authentication and client headers to the public status endpoint. Perform this check when creating a matchmaking ticket and again during in-game authorization.

const arcAuth = c.req.header("x-arc-auth");
const arcClient = c.req.header("x-arc-client");

if (!arcAuth || !arcClient) {
  return c.json([], 404);
}

const resp = await fetch(
  "https://dev-anticheat-v1.arc-services.dev/router/v1/anticheat/public/status",
  {
    method: "GET",
    headers: {
      "X-Arc-Client": arcClient,
      "X-Arc-Auth": arcAuth,
    },
  },
);

if (!resp.ok) {
  return c.text("", resp.status);
}
MatchmakingReject the ticket before allocating a server when Arc does not report an active protected client.
Game serverRecheck during authentication so a player cannot bypass the launcher by reusing only a matchmaking response.

Launcher setup

Create the Arc session in trusted launcher or backend code, then pass its token to the Arc process. Start Arc\Arc.exe and adding a -t=... argument.

1. Create the session

import * as arc from "arc-services";

const config: arc.Configuration = {
  ClientID: clientId,
  stage: "dev",
};

const instance = new arc.Instance(config);
const identity = instance.CreateIdentity(
  "MyGame",
  accountId,
  displayName,
);
const session = await instance.CreateAuthSession(identity);

const arcArgument = `-t=${session.auth.token}`;

2. Download and place Arc

The currently hosted executable is available at https://cloud.arc-services.dev/modules/anticheat/Arc.exe. Place Config.json in the same directory as Arc.exe.

GameBuild/
|-- Arc/
|   |-- Arc.exe
|   |-- Config.json
|   +-- Splash/
|       +-- Splash.png
+-- Game/
    +-- Binaries/
        +-- Win64/
            +-- GameClient.exe

3. Create Config.json

The executable path is relative to the game build containing the Arc directory. The splash path is optional and is resolved from the Arc executable directory.

{
  "ClientID": "your client id",
  "Executable": "Game\\Binaries\\Win64\\GameClient.exe",
  "Splash": "Splash\\Splash.png"
}

4. Start Arc with the session

import { spawn } from "node:child_process";
import path from "node:path";

const arcExecutable = path.join(buildPath, "Arc", "Arc.exe");

const arcProcess = spawn(
  arcExecutable,
  [
    ...gameArguments,
    `-t=${session.auth.token}`,
  ],
  {
    cwd: buildPath,
    windowsHide: true,
  },
);

Before you ship

  • Validate X-Arc-Auth on both callback routes.
  • Make ban and revoke operations idempotent.
  • Check status during matchmaking and game authentication.
  • Create Arc sessions only in trusted code.
  • Pass session.auth.token as -t=<token>.
  • Keep Arc.exe and Config.json together.
Review the TypeScript SDK guide