Documentation

Public API Documentation

Public integration guides, API reference, and project setup notes for teams evaluating or implementing Paxmod. Learn about API performance optimization.

Quickstart

Start moderating your game's chat in a few minutes.

1Create a project

A project is one game or app, with its own settings and API key. In the console, open All projects, open Add New, and pick Text or Image. Give it a name and describe your game: what it is and who plays it.

Live moderation does not use the description. Player Context does: it helps the AI read your game's chat in context.

2Choose what to flag

Text projects have two modes. Image projects always use categories. Change these any time under the project's Settings, and try them in the Playground.

Turn categories on or off, then tune how sensitive each one is with its threshold in the Playground. Expect a few rounds of adjusting before it fits your game.

  • Sexual
  • Hate and Discrimination
  • Violence
  • Dangerous and Criminal Content
  • Self-Harm
  • Medical Advice
  • Financial Advice
  • Legal Advice
  • Personally Identifiable Information
  • Harassment
  • Threatening
  • Profanities
  • Spam
Add words that are fine in your game, like slang or item names, to the project's Allow list so they are never flagged.

3Send messages from your game

Call the API with each message before other players see it. Your API key is under the project's Settings, API keys. Keep it on your server, never in the game client.

Example
// Check a message before it is shown to other players
const checkMessage = async (message, userId, conversationId) => {
  const response = await fetch('https://www.paxmod.com/api/v1/text', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      message,
      user_id: userId,          // the player who sent it
      context_id: conversationId // the same for everyone in the conversation
    })
  });

  const result = await response.json();

  if (result.content_moderation?.flagged) {
    return false; // block, hide or replace it
  }
  return true; // safe to show
};
Send user_id and context_id with every message. They power the Users page and Player Context. Use one context_id per conversation, the same for everyone in it (for a private chat, both players), so a conversation can be read as a whole.

4Review what Paxmod did

Each project's pages in the console show what happened:

  • Overview: messages and flags over time, what was flagged and who for.
  • Messages: every flagged message. Confirm the ones Paxmod got right and clear the ones it got wrong.
  • Users: the players behind the flags, and the ones you have warned or banned.
  • Cases: with Player Context on, conversations that look risky across many messages.

You're set

Questions or feedback? Email contact@paxmod.com.

Was this page helpful?

Projects

What a project decides, and how each setting changes what gets flagged.

Text projects

A project is one game or app, with its own API key, settings and data. Create one from All projects, Add New. Text projects have two modes, and you can switch between them any time in the project's Settings.

Standard

Each message is scored from 0 to 1 in every category you turn on, and each category has its own threshold. Best when your rules match common categories.

Advanced

You write your moderation policy in plain language (up to 800 characters). Each message gets one Policy Violation score and a written reason. Best for rules that fixed categories do not cover.

Standard mode categories:

  • Sexual
  • Hate and Discrimination
  • Violence
  • Dangerous and Criminal Content
  • Self-Harm
  • Medical Advice
  • Financial Advice
  • Legal Advice
  • Personally Identifiable Information
  • Harassment
  • Threatening
  • Profanities
  • Spam
Profanities is a word filter that also catches common disguised spellings, and scores 0.8 when it matches. The other categories are scored by AI models.

Thresholds

A message is flagged in a category when its score is above that category's threshold. Every threshold starts at 0.5.

  • Lower threshold: stricter. More messages are flagged, including some that are fine.
  • Higher threshold: more permissive. Fewer messages are flagged, and some harmful ones get through.

Sample message: “you're trash, uninstall”

Flagged for Harassment
Harassment
0.82 vs 0.50Flagged
Threatening
0.22 vs 0.50Passes
Hate and Discrimination
0.06 vs 0.50Passes
Profanities
0.00 vs 0.50Passes
Drag a threshold. Sample scores, not a live check. Try your own messages in your project's Playground.

Thresholds are set in the project's Playground: test a message, see each category's score, move the sliders, and save. In advanced mode there is one threshold, for Policy Violation. Saved changes can take up to 5 minutes to reach live traffic.

Turning a category off means it is not checked at all, so nothing is ever flagged for it. It does not make checks faster: the AI models run for every message either way.

Allow list

Terms that are safe in your game but look risky out of context, like item names, slang or game mechanics. Matching is whole word and case insensitive, in both modes. Each term has its own behaviour:

  • Replace word: swapped for a neutral word you choose, then the message is checked. Usually the best choice, because the sentence still reads naturally.
  • Remove word: dropped, and the rest of the message is still checked.
  • Skip moderation: any message containing the term is returned not flagged, with no category scores.

When a term is removed or replaced, the response includes allow_list (what was applied), and request.processed_message shows the exact text that was scored. If a message is made up only of allowed terms, the original message is checked instead.

Skip moderation is a blind spot: a message with that term passes however else it reads. Prefer replace or remove unless you want the whole message exempt.

Image projects

Image projects check images against their own set of categories, each with a threshold (0.5 to start). Create one from Add New, Image moderation, and choose the categories to check. Image projects have no advanced mode and no allow list.

  • Sexual
  • Sexual/Minors
  • Violence
  • Violence/Graphic
  • Self-Harm
  • Self-Harm/Intent
  • Self-Harm/Instructions
  • Harassment
  • Harassment/Threatening
  • Hate
  • Hate/Threatening
  • Illicit
  • Illicit/Violent

Testing

Before going live, run real examples from your game through the Playground, including messages that should pass: slang, banter, trading talk. Tests run against the last saved settings. They count as calls, and flagged tests appear on the Messages page under the player playground.

What the API returns

result is flagged or not_flagged, and content_moderation.categories has the score and threshold for each category checked. In standard mode, degraded is true when an AI provider was unavailable and some categories could not be scored; those are listed in unavailable_categories and are not flagged.

Standard mode:

{
  "status": "success",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "service": "pax",
  "request": {
    "original_message": "you're trash, uninstall",
    "processed_message": "you're trash, uninstall",
    "timestamp": "2026-10-16T09:12:44.120Z"
  },
  "result": "flagged",
  "reason": "Content flagged for Harassment",
  "degraded": false,
  "unavailable_categories": [],
  "content_moderation": {
    "flagged": true,
    "categories": {
      "Harassment": { "flagged": true, "threshold": 0.5, "score": 0.8234 },
      "Threatening": { "flagged": false, "threshold": 0.5, "score": 0.2156 },
      "Profanities": { "flagged": false, "threshold": 0.5, "score": 0 }
    }
  }
}

Advanced mode:

{
  "status": "success",
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "service": "pax",
  "request": {
    "original_message": "join my server for free robux, link in bio",
    "processed_message": "join my server for free robux, link in bio",
    "timestamp": "2026-10-16T09:12:44.120Z"
  },
  "result": "flagged",
  "reason": "Promotes a scam offering free currency, which the policy does not allow",
  "content_moderation": {
    "flagged": true,
    "categories": {
      "Policy Violation": { "flagged": true, "score": 0.91, "threshold": 0.5 }
    }
  }
}
Was this page helpful?

API reference

Two endpoints: one for text, one for images. JSON in, JSON out.

Authentication

Send your project's API key as a bearer token. You can also send it as api_key in the request body, for older integrations. The key is under the project's Settings, API keys.

Authorization: Bearer YOUR_API_KEY
Call the API from your server. A key in a game client can be read and used by anyone.

Keys for AI coding agents

A coding agent (Claude Code, Cursor, Codex and others) can create a working key with no account, build and test the integration, and hand you a link to keep it. The key covers text moderation in standard mode: 100 messages over 72 hours, at most 3 keys per address a day.

curl -X POST https://www.paxmod.com/api/v1/agent-key \
  -H "Content-Type: application/json" \
  -d '{"name": "My game chat", "source": "claude-code"}'

The response has api_key and claim_url. Opening the claim link adds the project to your account; start the 14-day free trial and the same key keeps working, with no code change. Until then, a used-up or expired key returns 402 AGENT_KEY_LIMIT_REACHED or 402 AGENT_KEY_EXPIRED.

POST /api/v1/text

Checks one message against the project's settings. The project's mode (standard or advanced) decides how.

Example
const response = await fetch('https://www.paxmod.com/api/v1/text', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    message: 'Text to moderate',
    user_id: 'player_123',     // optional, the player who wrote it
    context_id: 'chat_456'     // optional, the same for everyone in the conversation
  })
});

const result = await response.json();
if (result.result === 'flagged') {
  // block, hide or replace the message
}

Request body

FieldTypeDescription
messagestring, requiredThe text to check. Up to 5000 characters after whitespace is normalised.
user_idstringThe player who wrote it. Powers the Users page, bans and Player Context.
context_idstringThe conversation it was sent in. One ID per conversation, the same for everyone in it (for a private chat, both players).
advanced_thresholdnumber, 0 to 1Advanced mode only: overrides the project's Policy Violation threshold for this request.

Response

{
  "status": "success",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "service": "pax",
  "request": {
    "original_message": "Text to moderate",
    "processed_message": "Text to moderate",
    "timestamp": "2026-10-16T09:12:44.120Z"
  },
  "result": "flagged",
  "reason": "Content flagged for Harassment",
  "degraded": false,
  "unavailable_categories": [],
  "content_moderation": {
    "flagged": true,
    "categories": {
      "Harassment": { "flagged": true, "threshold": 0.5, "score": 0.8234 },
      "Threatening": { "flagged": false, "threshold": 0.5, "score": 0.2156 }
    }
  }
}
FieldDescription
resultflagged or not_flagged. Same as content_moderation.flagged.
reasonWhy. Standard mode: the categories that were flagged. Advanced mode: the AI's written reason.
content_moderation.categoriesScore (0 to 1) and threshold for each category checked. Flagged when the score is above the threshold. Advanced mode has one category, Policy Violation.
request.processed_messageThe exact text that was scored: normalised, with any allow list changes applied.
allow_listOnly when allow list terms were removed or replaced: each term, mode, replacement and count.
degraded, unavailable_categoriesStandard mode only. If an AI provider was unavailable, the request still succeeds, degraded is true, and the categories that could not be scored are listed and not flagged.

Some responses carry no category scores:

A message with an allow list term set to Skip moderation returns not_flagged with the reason Message contains whitelisted term. A message from a player you banned in Paxmod returns flagged with the reason User banned in Paxmod.

Errors

Errors return status: "error" and an error object with code, message, details and timestamp. One exception: a missing or blank message returns error as a plain string.

{
  "status": "error",
  "error": {
    "code": "MESSAGE_TOO_LONG",
    "message": "Message exceeds maximum length",
    "details": "The message must not exceed 5000 characters",
    "timestamp": "2026-10-16T09:12:44.120Z"
  }
}
HTTP and codeWhen
400 (no code)message is missing, not a string, or blank. Sent text instead? The details say so.
400 INVALID_JSONThe body is not valid JSON.
400 EMPTY_MESSAGENothing is left after normalising, for example a message of only invisible characters.
400 MESSAGE_TOO_LONGOver 5000 characters.
401 UNAUTHORIZEDNo API key.
401 INVALID_API_KEYThe key does not match a project.
402 PLAN_REQUIREDThe workspace has no active plan.
402 TRIAL_LIMIT_REACHEDThe free trial's message allowance is used up.
402 PREVIEW_LIMIT_REACHEDThe preview allowance is used up.
403 PLAN_UPGRADE_REQUIREDThe plan does not include this, for example image moderation below Studio.
402 AGENT_KEY_LIMIT_REACHEDAn agent key has used its 100 messages. Claim it and start the trial to keep going.
402 AGENT_KEY_EXPIREDAn agent key is past its 72 hours. Claim it and start the trial to keep going.
429 USAGE_LIMIT_EXCEEDEDOlder free accounts: the monthly free message limit is reached.
503 LLM_SERVICE_ERRORAdvanced mode: the AI model failed. The provider's own code and status can come through instead, such as a 429.
500 INTERNAL_SERVER_ERRORSomething went wrong on our side.

POST /api/v1/image

Checks one image, by URL, against an image project's categories and thresholds. The project must be an image project, and on current plans image moderation is part of Studio.

Example
const response = await fetch('https://www.paxmod.com/api/v1/image', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    image_url: 'https://example.com/image.jpg',
    user_id: 'player_123',     // optional
    context_id: 'upload_456'   // optional
  })
});

const result = await response.json();
FieldTypeDescription
image_urlstring, requiredA URL Paxmod can fetch the image from. It must be publicly reachable.
user_idstringThe player who sent or uploaded it.
context_idstringWhere it was sent, such as a conversation or upload.
{
  "status": "success",
  "id": "12f8d080-021e-4a3d-8f07-bda632fca263",
  "service": "pax",
  "request": {
    "image_url": "https://example.com/image.jpg",
    "timestamp": "2026-10-16T09:12:44.120Z"
  },
  "result": "not_flagged",
  "reason": "No moderation category thresholds exceeded",
  "content_moderation": {
    "flagged": false,
    "categories": {
      "Sexual": { "flagged": false, "threshold": 0.5, "score": 0.0023 },
      "Violence/Graphic": { "flagged": false, "threshold": 0.5, "score": 0.0145 }
    }
  }
}

Categories: Sexual, Sexual/Minors, Violence, Violence/Graphic, Self-Harm, Self-Harm/Intent, Self-Harm/Instructions, Harassment, Harassment/Threatening, Hate, Hate/Threatening, Illicit and Illicit/Violent.

HTTP and codeWhen
400 MISSING_IMAGE_URLNo image_url.
400 INVALID_IMAGE_URLimage_url is not a valid URL.
400 IMAGE_URL_UNAVAILABLEThe image could not be fetched from the URL.
400 INVALID_MODERATION_TYPEThe project is a text project.
400 INVALID_JSONThe body is not valid JSON.
401, 402, 403, 429, 500As for text.
Was this page helpful?