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.
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
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.
// 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
};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.
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
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 HarassmentThresholds 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.
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.
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 }
}
}
}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_KEYKeys 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.
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
| Field | Type | Description |
|---|---|---|
| message | string, required | The text to check. Up to 5000 characters after whitespace is normalised. |
| user_id | string | The player who wrote it. Powers the Users page, bans and Player Context. |
| context_id | string | The conversation it was sent in. One ID per conversation, the same for everyone in it (for a private chat, both players). |
| advanced_threshold | number, 0 to 1 | Advanced 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 }
}
}
}| Field | Description |
|---|---|
| result | flagged or not_flagged. Same as content_moderation.flagged. |
| reason | Why. Standard mode: the categories that were flagged. Advanced mode: the AI's written reason. |
| content_moderation.categories | Score (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_message | The exact text that was scored: normalised, with any allow list changes applied. |
| allow_list | Only when allow list terms were removed or replaced: each term, mode, replacement and count. |
| degraded, unavailable_categories | Standard 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 code | When |
|---|---|
| 400 (no code) | message is missing, not a string, or blank. Sent text instead? The details say so. |
| 400 INVALID_JSON | The body is not valid JSON. |
| 400 EMPTY_MESSAGE | Nothing is left after normalising, for example a message of only invisible characters. |
| 400 MESSAGE_TOO_LONG | Over 5000 characters. |
| 401 UNAUTHORIZED | No API key. |
| 401 INVALID_API_KEY | The key does not match a project. |
| 402 PLAN_REQUIRED | The workspace has no active plan. |
| 402 TRIAL_LIMIT_REACHED | The free trial's message allowance is used up. |
| 402 PREVIEW_LIMIT_REACHED | The preview allowance is used up. |
| 403 PLAN_UPGRADE_REQUIRED | The plan does not include this, for example image moderation below Studio. |
| 402 AGENT_KEY_LIMIT_REACHED | An agent key has used its 100 messages. Claim it and start the trial to keep going. |
| 402 AGENT_KEY_EXPIRED | An agent key is past its 72 hours. Claim it and start the trial to keep going. |
| 429 USAGE_LIMIT_EXCEEDED | Older free accounts: the monthly free message limit is reached. |
| 503 LLM_SERVICE_ERROR | Advanced mode: the AI model failed. The provider's own code and status can come through instead, such as a 429. |
| 500 INTERNAL_SERVER_ERROR | Something 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.
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();| Field | Type | Description |
|---|---|---|
| image_url | string, required | A URL Paxmod can fetch the image from. It must be publicly reachable. |
| user_id | string | The player who sent or uploaded it. |
| context_id | string | Where 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 code | When |
|---|---|
| 400 MISSING_IMAGE_URL | No image_url. |
| 400 INVALID_IMAGE_URL | image_url is not a valid URL. |
| 400 IMAGE_URL_UNAVAILABLE | The image could not be fetched from the URL. |
| 400 INVALID_MODERATION_TYPE | The project is a text project. |
| 400 INVALID_JSON | The body is not valid JSON. |
| 401, 402, 403, 429, 500 | As for text. |