The basic check
The built-in template every project has. It screens one text or image against eight policies and runs whenever a request names no template.
Every project has one template nobody created: the basic check. It screens a text or an image for the violations almost every product cares about, with eight policies and no rules of your own.
It runs whenever a request names no template, so a first request works in a project where nothing is configured. On the Free plan it is the only template that runs.
curl https://api.useinfrared.com/v1/moderations \
-H "Authorization: Bearer $INFRARED_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "Prescription painkillers, no prescription needed. Discreet shipping worldwide."
}'const response = await fetch("https://api.useinfrared.com/v1/moderations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.INFRARED_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
input: "Prescription painkillers, no prescription needed. Discreet shipping worldwide.",
}),
});
const moderation = await response.json();import os
import requests
response = requests.post(
"https://api.useinfrared.com/v1/moderations",
headers={"Authorization": f"Bearer {os.environ['INFRARED_KEY']}"},
json={
"input": "Prescription painkillers, no prescription needed. Discreet shipping worldwide.",
},
)
moderation = response.json(){
"object": "moderation",
"id": "mod_36pVSjVX2aRCRutzyGT1lE",
"template": { "slug": "basic", "version": 1 },
"type": "text",
"status": "completed",
"flagged": true,
"recommendation": "reject",
"severity": "critical",
"policies": {
"toxicity": 0,
"hate_speech": 0,
"profanity": 0,
"violence": 0,
"self_harm": 0,
"sexual": 0,
"illicit": 0.95,
"spam": 0.8
},
"triggered": ["illicit", "spam"],
"rule": null,
"reasons": ["trigger:illicit:flag", "trigger:spam:review", "triage:reject"],
"details": {
"illicit": {
"labels": ["prescription_meds"],
"matches": [
{ "label": "prescription_meds", "value": "Prescription painkillers, no prescription needed." },
{ "label": "prescription_meds", "value": "Discreet shipping worldwide." }
]
},
"spam": {
"labels": ["promotion"],
"matches": [
{ "label": "promotion", "value": "Prescription painkillers, no prescription needed. Discreet shipping worldwide." }
]
}
},
"usage": { "checks": 0, "cached": false },
"test": true
}template says the basic check answered. illicit reached 0.95 and flagged the text. spam reached 0.80, and in the basic check spam asks for a review and does not flag. With no rule to decide, severity triage read the highest score and rejected.
details quotes what each policy found. Show its labels to a moderator and do not branch on them: some come from an automatic safety pass, and their exact strings can change. The policy catalog says what to rely on.
The request was made with a test key, so usage.checks is 0. With a live key it uses one check.
What it checks
| Policy | When it reaches 0.70 | Screens |
|---|---|---|
toxicity | Flags | Text |
hate_speech | Flags | Text |
profanity | Flags | Text |
violence | Flags | Text and images |
self_harm | Flags | Text and images |
sexual | Flags | Text and images |
illicit | Flags | Text and images |
spam | Sends to review | Text |
The response lists all eight under policies. One that does not apply to the content, such as spam on a photo, scores 0. Text found inside an image is read too, and checked against the list of swear words.
There are no content rules, so triage decides every time: allow below 0.40, review from 0.40, reject from 0.80. profanity and spam count for 0.8 of their score there, and the other six in full. The policy catalog describes each policy.
What it leaves out
The basic check does not look for contact details, personal data, links, off-topic content or duplicates, and it has no guidelines, wordlists, AI instructions or content rules.
That is on purpose. Whether a phone number is a violation depends on the product, and saying so is what a template is for.
When it runs
- A request names no template and the project has no default template.
- A request names it:
"template": "basic". - Every request on the Free plan. There a project's default template is ignored without an error, and a request that names any other template answers 403
plan_limit.
What you can send
| Shape | Request body |
|---|---|
| One text | { "input": "..." } |
| One image | { "type": "image", "input": "https://..." }, with exactly one URL |
| A form | { "fields": { "text": "...", "image": ["https://..."] } }, with either field or both |
The form's image field takes up to ten URLs. Any other key under fields makes the submission invalid with unknown_field.
A request that carries an image answers 202 and is screened in the background. Moderate images and Moderate a whole form show both shapes with their responses.
It cannot be changed
The basic check is the same in every project and has nothing to configure. In Templates it leads the list with a Built in badge. Its tabs show what it holds and accept no edits, and it cannot be renamed or archived. The Test tab works.
It does not count toward your plan's limit of templates. To set your own fields, policies or rules, create a template.
Read it over the API
The basic check is returned like any template, first in GET /v1/templates and by its slug.
curl https://api.useinfrared.com/v1/templates/basic \
-H "Authorization: Bearer $INFRARED_KEY"const response = await fetch("https://api.useinfrared.com/v1/templates/basic", {
headers: { Authorization: `Bearer ${process.env.INFRARED_KEY}` },
});
const template = await response.json();import os
import requests
response = requests.get(
"https://api.useinfrared.com/v1/templates/basic",
headers={"Authorization": f"Bearer {os.environ['INFRARED_KEY']}"},
)
template = response.json(){
"object": "template",
"id": "tpl_7CMEFMWJV8VbLGhdSoRUUB",
"slug": "basic",
"name": "Basic check",
"description": "The built-in check of one text or image for common violations. It runs when a request names no template.",
"status": "active",
"built_in": true,
"version": 1,
"context": null,
"fields": [
{ "key": "text", "name": "Text", "type": "text", "required": false, "triggers": [], "validation": {} },
{ "key": "image", "name": "Image", "type": "image", "required": false, "triggers": [], "validation": { "max_bytes": 10485760, "max_items": 10 } }
],
"policies": [
{ "key": "toxicity", "mode": "trigger" },
{ "key": "hate_speech", "mode": "trigger" },
{ "key": "profanity", "mode": "trigger" },
{ "key": "violence", "mode": "trigger" },
{ "key": "self_harm", "mode": "trigger" },
{ "key": "sexual", "mode": "trigger" },
{ "key": "illicit", "mode": "trigger" },
{ "key": "spam", "mode": "trigger", "effect": "review" }
],
"triggers": [],
"rules": [],
"severity": { "reject": 0.8, "review": 0.4 },
"ai_instructions": null,
"settings": {},
"created_at": "2026-10-08T08:44:19.741Z",
"updated_at": "2026-10-08T08:44:19.894Z"
}built_in is true for this template only. Its version can rise: when Infrared changes the basic check, each project gets a new version of it, and earlier moderations keep the version they ran on. Do not expect version to stay 1.
What it costs
One check per object, and one more per image. A text is one check and a single image is two. Checks and usage has the arithmetic.
Next
- QuickstartCreate an API key, send one request and read the verdict. It works on the Free plan, in a project where nothing is configured yet.
- TemplatesA template is the configuration for one kind of content. It holds the fields, the policies and the rules, and requests name it by its slug.
- Moderate imagesSend an image by URL. Infrared fetches it, screens the picture and reads the text inside it. The result arrives by webhook or by polling.
- Plans and limitsWhat each plan includes, what is counted per organisation and what per project, and what happens to your setup when the plan changes.
Something missing or unclear on this page? Write to support@useinfrared.com.
