Authors, content ids and metadata
Five optional fields tie a moderation to your own data. They say who wrote the content, which object it is, what it belongs to and whether it is stored.
A moderation is more useful when it knows where the content came from. Five optional fields say so: who wrote it, which object of yours it is, what it belongs to, anything else you want to carry along, and whether the content may be stored. Each works with every template and on every plan, and your ids and metadata come back with the moderation wherever you read it.
curl https://api.useinfrared.com/v1/moderations \
-H "Authorization: Bearer $INFRARED_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "text",
"input": "Is the table still available? I could collect it on Saturday morning.",
"author_id": "user_42",
"content_id": "message_77120",
"context_id": "conversation_5531",
"metadata": {
"client": "ios",
"app_version": "4.12.0"
}
}'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({
type: "text",
input: "Is the table still available? I could collect it on Saturday morning.",
author_id: "user_42",
content_id: "message_77120",
context_id: "conversation_5531",
metadata: {
client: "ios",
app_version: "4.12.0",
},
}),
});
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={
"type": "text",
"input": "Is the table still available? I could collect it on Saturday morning.",
"author_id": "user_42",
"content_id": "message_77120",
"context_id": "conversation_5531",
"metadata": {
"client": "ios",
"app_version": "4.12.0",
},
},
)
moderation = response.json(){
"object": "moderation",
"id": "mod_7JETFVvNC1PmPgQRu9ecxh",
"status": "completed",
"flagged": false,
"recommendation": "allow",
"author": { "id": "aut_1reK9hfk33nRTsdZpGN25v", "external_id": "user_42", "status": "enabled", "trust_level": "new", "known": true },
"content_id": "message_77120",
"context_id": "conversation_5531",
"metadata": { "client": "ios", "app_version": "4.12.0" }
}author_idstringOptionalYour id for the person who wrote the content. Up to 200 characters.
content_idstringOptionalYour id for the content itself: the listing, the message, the review. Up to 200 characters.
context_idstringOptionalYour id for what the content belongs to: a conversation, a thread, an order. Up to 200 characters.
metadataobjectOptionalUp to 50 keys of your own. A key has up to 64 characters. A value is a string of up to 1,000 characters, a number, a boolean or
null: no nested objects and no lists.storebooleanOptionalfalsekeeps the content of this moderation out of storage.
Say who wrote it
The first moderation that carries an unknown author_id creates an author in the project, with status enabled and trust level new. From then on the author is addressed by your id, and the author object in each response shows the status and trust level the decision used.
Sending the author does four things:
- Content rules can read it. A rule can block a suspended author or let a trusted one through. A request without
author_idis judged as an enabled author with the lowest trust level. - History builds up. Each finished moderation adds to the author's counters, and the trust level follows from them. Moderations sent with a test key, dry runs and invalid submissions do not count.
- Duplicates are told apart. A repeat by the same author scores lower than the same content posted by somebody else.
- Reviewers see who it is. The review queue item and the author's page in the dashboard list the moderation.
A template's Require an author id setting is not enforced today: a request without author_id is accepted on such a template too.
Name the content
content_id is your handle on the object. It does not have to be unique: each edit of a listing can be moderated under the same id, and each moderation is kept.
Duplicate detection uses it. Earlier moderations with the same content_id are left out of the comparison, so checking an edited object again is not a duplicate of itself. Without a content_id, the same text sent twice by the same author does count as one, where the template enables the duplicate policy. When a later submission is a duplicate, its details names the content_id of the earlier one.
While the result cache holds the first answer, which is six hours, an identical text sent again to the same template version gets the cached scores of the first run. duplicate therefore stays at 0 for it until the entry expires.
The review queue and the Live console show the id beside the content, and they show it in place of the content when the content was not stored.
Group related content
context_id ties moderations together: every message of a conversation, every reply in a thread. It is stored, returned and searchable. It does not change scores or decisions, and earlier content with the same context_id is not shown to the models.
Attach your own data
metadata is carried with the moderation and returned wherever the moderation is: in the response, in the list, in webhook events. Policies and rules do not read it and the list cannot be searched by it. It is removed with the rest of the stored content on the moderation's retention date.
Keep the content out of storage
Content is stored when the plan, the project and the template all allow it. store: false turns storage off for one request. It cannot turn it on: store: true on a plan or in a project that stores nothing changes nothing.
A moderation that is not stored keeps no text, no image and no source URL. Its scores, its decision and a hash of the content remain, so the result cache and duplicate detection still work. Three things follow:
- the dashboard and the review queue show no preview of it,
- it cannot be re-moderated,
- what the policies quoted is still kept:
details, with its matches and masked text, stays with the moderation until its retention date.
Data retention covers what each plan keeps and for how long.
Find moderations later
GET /v1/moderations lists the project's moderations, newest first, and takes your ids as filters.
curl "https://api.useinfrared.com/v1/moderations?context_id=conversation_5531&limit=50" \
-H "Authorization: Bearer $INFRARED_KEY"const response = await fetch("https://api.useinfrared.com/v1/moderations?context_id=conversation_5531&limit=50", {
headers: { Authorization: `Bearer ${process.env.INFRARED_KEY}` },
});
const page = await response.json();import os
import requests
response = requests.get(
"https://api.useinfrared.com/v1/moderations",
headers={"Authorization": f"Bearer {os.environ['INFRARED_KEY']}"},
params={
"context_id": "conversation_5531",
"limit": 50,
},
)
page = response.json(){
"object": "list",
"data": [
{
"object": "moderation",
"id": "mod_7JETFVvNC1PmPgQRu9ecxh",
"type": "text",
"status": "completed",
"flagged": false,
"recommendation": "allow",
"author": { "id": "aut_1reK9hfk33nRTsdZpGN25v", "external_id": "user_42" },
"content_id": "message_77120",
"context_id": "conversation_5531"
}
],
"has_more": false,
"next_cursor": null
}| Filter | Matches |
|---|---|
author_id, content_id, context_id | The id you sent, exactly |
template | The slug of the template that ran |
status | screening, completed, partial, failed or invalid |
recommendation | allow, review or reject |
type | text, image or form |
language | An ISO 639-1 code |
flagged, dry_run | true, or an empty value for the opposite |
created_from, created_to | A UTC time ending in Z, such as 2026-10-01T00:00:00Z |
A few things about the list:
flaggedanddry_runread any value that is not empty astrue, the wordfalseincluded. To list what was not flagged, send the parameter empty, as inflagged=. To get both, leave it out.- A date without a time, or a time with an offset, answers 422.
- The list holds live and test moderations together. The
testfield tells them apart. - A form is one row, without its children. Fetch it by id for the fields.
Pages follow the usual cursor. To read one author's history, GET /v1/authors/{external_id}/moderations returns compact rows for that author alone.
Next
- Authors and trustInfrared keeps a status, a trust level and a history for each of your users, and content rules read them. Block, suspend or trust an author.
- Review queueContent your rules send to review waits in the queue until a person, or your own code, allows it, rejects it or runs another action on it.
- Data retention and privacyWhat Infrared keeps of a moderation, for how long, and what the nightly purge removes. Set it for a project, a template or a single request.
- Dry run and re-moderationRun your rules on real traffic without enforcing them, and run stored content again after a template changes. What each one records and what it costs.
Something missing or unclear on this page? Write to support@useinfrared.com.