Skip to content

Event types

The twelve events Infrared sends, when each one fires and what its data holds, with a real payload for moderations, queue items, authors and actions.

An event is one thing that happened in a project: a moderation finished, a queue item was decided, an author was suspended, an action ran. There are twelve types. An endpoint receives the ones it subscribed to, and your handler branches on type.

Every event has the same envelope. Only data changes with the type.

queue_item.rejected, trimmedJSON
{
  "id": "evt_3zOw19OFNY90sutpFKkCw1",
  "type": "queue_item.rejected",
  "created_at": "2026-10-08T08:47:47.909Z",
  "api_version": "2026-09-01",
  "data": {
    "action": "reject",
    "source": "api",
    "queue_item": {
      "id": "qi_2j7nkTPbA3YvLz4qpxmzPk",
      "state": "resolved",
      "object": "queue_item",
      "resolution": "rejected",
      "resolved_at": "2026-10-08T08:47:47.630Z",
      "moderation_id": "mod_1f4LShzWx2qKdBwHC7u5kM"
    },
    "resolution": "rejected",
    "moderation_id": "mod_1f4LShzWx2qKdBwHC7u5kM"
  }
}
idstring

evt_ and 22 characters. The same on every retry, on a replay and on every endpoint that receives the event. Use it to handle an event once.

typestring

One of the twelve types below.

created_atstring

When the event was recorded, in UTC.

api_versionstring

The version the payload follows: 2026-09-01.

dataobject

What happened. Its keys depend on type.

The examples on this page are real payloads, in the order their keys arrived. Inside data that is storage order, shortest names first, not the order the same object has in an API response. Read keys by name and never by position.

The twelve events

EventSent whendata
moderation.completedA moderation finishes. Its status is completed, partial or invalid.moderation
moderation.failedA run fails and the moderation ends with the status failed.moderation
queue_item.createdA moderation is sent to review and enters the queue.queue_item, moderation_id
queue_item.allowedA queue item is resolved with the allow action.queue_item, moderation_id, resolution, action, source
queue_item.rejectedA queue item is resolved with the reject action.The same five.
queue_item.actionedA queue item is resolved with an action of your own.The same five.
queue_item.reopenedA resolved item is reopened.queue_item, moderation_id
author.blockedAn author's status becomes blocked.author, previous_status, reason, until, source
author.suspendedAn author's status becomes suspended.The same five.
author.enabledAn author's status becomes enabled, by a request or because a suspension reached its end.The same five.
author.trust_level_changedAn author's trust level changes, from their history or from an override.author, previous_trust_level, trust_level, source
action.executedAn action runs, from the API or the dashboard.action, moderation_id, author_id, content_id, queue_item_id, value, source, executed_at

An endpoint with an empty event_types list receives all of them. Ignore a type your handler does not know, so that a new one never breaks it.

Moderation events

data.moderation is the moderation object, the same body the API returned for it.

moderation.completed, trimmedJSON
{"id": "evt_0GQHhLiYtDqcwozYf0gbqe","type": "moderation.completed","created_at": "2026-10-08T08:48:16.317Z","api_version": "2026-09-01","data": {"moderation": {"id": "mod_2wCRB1Sodc8XV83zmnJ6x6","rule": { "id": "r6", "name": "Links from new accounts", "action": "review" },"test": true,"type": "text","usage": { "cached": false, "checks": 0 },"author": null,"object": "moderation","status": "completed","details": {"guideline/no-off-site-deals": {"labels": ["confidence:100"],"matches": [{"field": null,"label": "evidence","value": "Send the deposit to my bank account and I will hold it for you."}],"explanation": "The seller explicitly requests a deposit to their bank account, which is a clear attempt to move the transaction off-platform."}},"dry_run": false,"flagged": true,"reasons": ["trigger:links_contact:flag","trigger:guideline/no-off-site-deals:flag","rule:r6"],"language": "en","metadata": null,"policies": { "toxicity": 0, "links_contact": 0.7, "guideline/no-off-site-deals": 1 },"severity": "critical","template": { "slug": "listing", "version": 1 },"triggered": ["links_contact", "guideline/no-off-site-deals"],"content_id": null,"context_id": null,"created_at": "2026-10-08T08:48:12.475Z","latency_ms": 2500,"completed_at": "2026-10-08T08:48:15.121Z","recommendation": "review"}}}
  • Read status before recommendation. moderation.completed is sent for every moderation that finishes, including one that ends partial because a policy did not answer, and one that ends invalid because the submission failed the template's validation. Statuses and failures says what each means.
  • moderation.failed carries the moderation with status: "failed", an error object whose code is pipeline_error, no scores and a recommendation of null. Treat the content as not screened.
  • A whole form is one event. The moderation of a form carries the result of each field under fields. Each item of a batch is its own moderation and its own event.
  • The event is sent whether or not the request waited. A text answered in the same response sends it too, so one handler can serve both paths.

The payload quotes what the policies matched. The event log keeps it for 30 days on every plan, whatever the project's retention is.

Queue item events

data.queue_item is the queue item as it stands after the move. moderation_id repeats the id of the moderation that the item's summary describes.

queue_item.created, trimmedJSON
{"id": "evt_0TzNdRH4XaX8ncewSgoZDH","type": "queue_item.created","created_at": "2026-10-08T08:48:16.033Z","api_version": "2026-09-01","data": {"queue_item": {"id": "qi_6B6ffFjJmuKtYExjbxpxgu","state": "pending","object": "queue_item","reason": "rule","summary": {"type": "text","author": null,"scores": { "toxicity": 0, "links_contact": 0.7, "guideline/no-off-site-deals": 1 },"dry_run": false,"preview": "Solid oak desk, barely used. Send the deposit to my bank account and I will hold it for you.","language": "en","severity": "critical","template": "listing","triggered": ["links_contact", "guideline/no-off-site-deals"],"content_id": null},"assignee": null,"language": "en","priority": "urgent","severity": "critical","author_id": null,"created_at": "2026-10-08T08:48:15.782Z","project_id": "prj_0TBAgHGtdMketrbltGFHuk","resolution": null,"sla_due_at": null,"updated_at": "2026-10-08T08:48:15.782Z","assigned_at": null,"reopened_at": null,"resolved_at": null,"resolved_by": null,"template_id": "tpl_64mYq4nD3NXNfu0gcdaMZa","moderation_id": "mod_2wCRB1Sodc8XV83zmnJ6x6"},"moderation_id": "mod_2wCRB1Sodc8XV83zmnJ6x6"}}

This item belongs to the moderation in the example above it. The moderation was made with a test key, and the queue item says nothing about that: a queue event carries no test flag, so look the moderation up by moderation_id when you need to know.

The three events that resolve an item add three keys, as in the example at the top of the page.

KeyValue
resolutionallowed, rejected, or action: followed by the action's key, for example action:request_edits.
actionThe key of the action that resolved the item: allow, reject or your own.
sourceapi or dashboard.

queue_item.resolved_by is the id of the member who decided in the dashboard. It is null when an API key did.

Resolving an item that is already resolved answers 200 and changes nothing. No queue_item event is sent for it, but the action runs again and action.executed is sent again.

Author events

data.author is the author object after the change.

author.suspended, trimmedJSON
{
  "id": "evt_4GJX2vnoy66uDEpBcmeYqZ",
  "type": "author.suspended",
  "created_at": "2026-10-08T08:49:05.850Z",
  "api_version": "2026-09-01",
  "data": {
    "until": "2026-10-13T00:00:00.000Z",
    "author": {
      "id": "aut_6xtGWa266HzKklUSuX97vC",
      "object": "author",
      "status": "suspended",
      "created_at": "2026-10-08T08:47:22.833Z",
      "project_id": "prj_0TBAgHGtdMketrbltGFHuk",
      "updated_at": "2026-10-08T08:49:05.663Z",
      "external_id": "user_8841",
      "trust_level": "new",
      "display_name": "Jordan Lee",
      "status_until": "2026-10-13T00:00:00.000Z",
      "status_reason": "Three rejected listings in one day",
      "trust_override": null,
      "trust_level_automatic": "new"
    },
    "reason": "Three rejected listings in one day",
    "source": "api",
    "previous_status": "enabled"
  }
}
  • previous_status is the status before the change. Setting an author to the status they already have sends nothing.
  • until is set only for a suspension with an end date.
  • source is api, dashboard or rule. rule marks a suspension that lifted itself: within 15 minutes of its end date Infrared sends author.enabled with reason: "suspension expired".
  • author.trust_level_changed carries previous_trust_level and trust_level, each one of new, basic, regular, established and trusted. Its source is automatic after a moderation and manual after an override.

Blocking or suspending an author also drops their automatic trust level to new. No author.trust_level_changed is sent for that drop: read data.author.trust_level in the status event.

Action events

action.executed is sent every time an action runs, whatever its target.

action.executedJSON
{"id": "evt_4m24Cq6qE5xjatO20hxDK6","type": "action.executed","created_at": "2026-10-08T08:48:22.365Z","api_version": "2026-09-01","data": {"value": { "reason": "off_platform_link", "message": "Remove the link from the description." },"action": {"id": "act_2BdP4rjezzQ5PTND66nyLi","key": "request_edits","name": "Request edits","object": "action","target": "content","enabled": true,"built_in": false,"position": 5,"created_at": "2026-10-08T08:44:38.841Z","project_id": "prj_0TBAgHGtdMketrbltGFHuk","updated_at": "2026-10-08T08:44:38.841Z","description": "Send the content back to its author with what to change. Your webhook receives the message.","value_schema": null,"accepts_value": true,"queue_behavior": "resolve","visible_in_dashboard": true},"source": "api","author_id": null,"content_id": "listing_20932","executed_at": "2026-10-08T08:48:22.177Z","moderation_id": "mod_6IYSa2qBKpMi4Xnxm1jkOB","queue_item_id": "qi_0eietVVV87wOZub5qkiPfU"}}
  • action is the whole action object, not only its key.
  • content_id is the id you sent with the moderation. author_id is Infrared's id of the author, starting with aut_, not the id you use for them. It is null for an action on content.
  • value is whatever JSON the run carried, unchecked. Validate it in your handler.
  • The note a moderator typed and the person who ran the action are not in the event.

One request, several events

Some requests send more than one event, each to be handled on its own.

What happenedEvents, in the order they are recorded
A moderation is sent to reviewauthor.trust_level_changed when the level moved, queue_item.created, then moderation.completed
A queue item is resolvedqueue_item.allowed, queue_item.rejected or queue_item.actioned, then action.executed
The block_author, suspend_author or enable_author action runsThe author.* event, then action.executed

Deliveries run side by side and a failed one is retried later, so events can arrive in another order. When order matters, compare created_at, or read the current state from the API.

Test keys and dry runs

A test key and a dry run both send events. Tell them apart in the payload.

TrafficModeration and queue eventsAutomatic trust change
Live keySentSent when the level moves
Test keySent, with test: true in the moderationNot sent: author history is not updated
Dry runSent, with dry_run: true in the moderation and reason: "dry_run" on the queue itemNot sent
Template testNothing is sentNothing is sent

Everything after that is the same for all of them. Resolving a queue item that a test moderation created sends the same events as any other.

Read past events

Every event is kept for 30 days, whether or not the project has an endpoint. List events returns them newest first: filter with type, and page with limit and cursor as on every list.

curl "https://api.useinfrared.com/v1/webhooks/events?type=moderation.completed&limit=1" \
  -H "Authorization: Bearer $INFRARED_KEY"
200 OK, trimmedJSON
{"object": "list","data": [{"object": "webhook_event","id": "evt_0mEfEDs3uCiqRA34OUEL5C","type": "moderation.completed","created_at": "2026-10-08T08:55:03.172Z","api_version": "2026-09-01","data": {"moderation": { "id": "mod_7ZgXCne4LiwQrsodlx4R8U", "object": "moderation", "status": "completed" }}}],"has_more": true,"next_cursor": "WyIyMDI2LTEwLTA4VDA4OjU1OjAzLjE3MloiLCIxOTkwMWIzZS0wYTBiLTQ0MzEtYTlhMy0xZGQ5ZGMwZjI4ODYiXQ"}

Use the list to catch up after an outage, or after an endpoint was disabled: those events were never sent to it and cannot be replayed. The dashboard has no screen for the event log.

Next

Something missing or unclear on this page? Write to support@useinfrared.com.