Moderate text in one call and images by webhook
Text and images go to the same endpoint: text returns 200 with the verdict, an image returns 202 and finishes by signed webhook or by polling.
- REST and JSON under /v1
- Secret keys, server to server
POST/v1/moderationsTextAnswered in the same request
- POST
{ "type": "text", "input": "Worst seller ever. You are a complete idiot." } - 200
{ "flagged": true, "recommendation": "reject", "triggered": ["toxicity"], … }
ImageAccepted, then finished by webhook
- POST
{ "type": "image", "input": "https://example.com/1.jpg" } - 202
{ "status": "screening", "recommendation": null, … } POST https://hooks.example.com/infraredYour endpointNot sent yet- Infrared-Event
- moderation.completed
- Infrared-Signature
- t=1790942400,v1=<64 hex characters>
- data.moderation
- { "flagged": false, "recommendation": "allow", … }
Sample data
Send one object, a whole form or a batch
POST /v1/moderations takes a single object or a whole form, and /v1/moderations/batch takes a list of either.
- type
- Takes text or image, and defaults to text.
- fields
- A whole form of 1 to 40 fields. Your own fields need a template, on Starter and up.
- items
- 1 to 100 objects or forms in one batch call. Your plan sets the cap.
curl https://api.useinfrared.com/v1/moderations \
-H "Authorization: Bearer ir_live_…" \
-H "Content-Type: application/json" \
-d '{
"type": "text",
"input": "You are a complete idiot.",
"content_id": "review_18342",
"author_id": "user_42",
"metadata": { "source": "ios" }
}'curl https://api.useinfrared.com/v1/moderations \
-H "Authorization: Bearer ir_live_…" \
-H "Content-Type: application/json" \
-d '{
"template": "listings",
"fields": {
"title": "Vintage oak dining table, seats six",
"description": "Solid oak, light wear on one leg.",
"price": 450,
"photos": ["https://cdn.example.com/1.jpg"]
}
}'curl https://api.useinfrared.com/v1/moderations/batch \
-H "Authorization: Bearer ir_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: import-2026-10-02-0001" \
-d '{
"items": [
{ "type": "text", "input": "Great seller, fast delivery.", "content_id": "review_1" },
{ "type": "text", "input": "You are a complete idiot.", "content_id": "review_2" }
]
}'Retry a call without moderating twice
Send an Idempotency-Key with a moderation or a batch call, and a repeat with the same body within 24 hours returns the first response.
POST/v1/moderationsIdempotency-Key:7b1f0c52-upload-18342
First call
The text is moderated and the response is stored.
200 OK
Same key, same body
Nothing is moderated again.
200 OKIdempotent-Replayed: true
Same key, different body
The call is refused and nothing is moderated.
409 Conflictidempotency_key_reused
Sample data
Test keys run the full pipeline for free
An ir_test_ key works on the same project and templates as a live key, uses no checks and marks the response test: true.
The queue and webhooks still runA test call is recorded as a moderation, can enter the review queue and fires your webhooks.
Trust levels do not moveA test call is not counted in an author's history.
Counted apartUsage records test traffic separately from live.
Request header
Authorization: Bearer ir_test_…Runs the full pipeline, never consumes checks.
- Checks used by this call
- 0
- Checks this month
- 8,420 of 10,000
{ "id": "mod_1B7lY4kzM9n2Qx8sRt3wVu", "status": "completed", "flagged": true, "recommendation": "reject", "usage": { "checks": 0, … }, "dry_run": false, "test": true, …}Sample data
One error shape to branch on
Every error is the same envelope: a stable code, a message, details and the request id.
- invalid_api_key
- 401The key is malformed, unknown, revoked or expired.
- plan_limit
- 403A template other than the basic check on Free, or a batch over the plan's cap.
- validation_error
- 422A field is missing, mistyped or unknown. Each one is listed in details.
{"error": {"code": "quota_exceeded","message": "The monthly checks quota is exhausted and no extra checks are available","details": [{ "field": "checks", "message": "1 needed, 0 available" }],"request_id": "req_5f0c2b7e9a4d4c1b8e3f6a7d9c0b1e2f"}}X-RateLimit-Limit: 60X-RateLimit-Remaining: 0X-RateLimit-Reset: 1790942460Retry-After: 23{"error": {"code": "rate_limited","message": "Rate limit exceeded for this API key","details": [],"request_id": "req_9d3a7c1e5b2f4a8c6e0d1b3f5a7c9e2d"}}
X-Request-IdOn every response. A moderation keeps it, and the dashboard shows it as Request.
Sample data

Start moderating your users' content
Create an API key, send your first request and get a verdict back.