Skip to content

Actions for your team, signed webhooks for your backend

Every action a moderator or your code runs is logged and sent to your endpoints as an event.

  • On every plan
  • Signed with HMAC-SHA256
What one action does
Sample data
Review queue

A moderator runs Request edits on this item.

Film camera, barely used. Call me on +44 7700 900123

listings · English · listing_48213

PendingNormalContent rule
RejectAllowMore actions
  • Block author
  • Suspend author
  • Enable author
  • Request edits
  • Assign to me
  • Open in live console
Action log
  • request_editsjust nowContent mod_1B7l…3wVuDashboard
  • reject1 min agoContent mod_1B7l…3wVuDashboard
  • allow4 min agoContent mod_6Wn3…Pq7aAPI
  • block_author1 hour agoAuthor aut_2Hd9…Xk4mDashboard
Your endpointexample.com
POST /hooks/infraredInfrared-Signature: t=1790863420,v1=<64 hex characters>Infrared-Event: action.executedInfrared-Delivery: whd_3k9pXa7Fm2Qw8Ls4Td6VbN{
  "type": "action.executed", …
  "data": {
    "action": { "key": "request_edits", … }, …
  }
}{
  "type": "action.executed", …
  "data": {
    "action": {
      "key": "request_edits", …
    }, …
  }
}
Response200Delivered

Sample data

Five built-in actions, and your own

Allow, Reject and the three author actions change Infrared's own records. An action you add is logged and sent as an event, and your backend does the rest.

  • An action can take a valueA reason as text, or JSON of your own. It is kept in the action log and sent in the webhook.

  • From a queue item or the APIA moderator runs it on the item, or your code calls POST /v1/actions/:key/execute.

Acme Corp./Actions
Sample data
The actions of a project: five built-in actions and one custom action
NameKeyTargetQueue behaviourVisible in dashboardEnabled
AllowBuilt-inallowallowContentResolve the queue item
RejectBuilt-inrejectrejectContentResolve the queue item
Block authorBuilt-inblock_authorblock_authorAuthorKeep it in the queue
Suspend authorBuilt-insuspend_authorsuspend_authorAuthorKeep it in the queue
Enable authorBuilt-inenable_authorenable_authorAuthorKeep it in the queue
Request editsrequest_editsAsk the seller to fix the listingrequest_editsContentResolve the queue item

Recent executions

Each execution is logged and sent to your webhooks as action.executed.

The two most recent action executions
ActionTargetSourceNoteActorWhen
request_editsContent mod_1B7l…3wVuDashboardSecond time this weekusr_6Tz2…kQ8m2 min ago
suspend_authorAuthor aut_5Kc2…RnTqAPINo noteAPI key1 hour ago

Sample data

Twelve webhook events tell your backend what changed

Each endpoint receives every event type, or only the ones you tick.

  • Kept for 30 daysRead events and deliveries back from the API.

  • Endpoints per project1 on Free, 3 on Starter, 10 on Growth and 50 on Business, each a public https URL.

Run on a queue item
Sample data
Moderation2
  • moderation.completed
  • moderation.failed
Author4
  • author.blocked
  • author.suspended
  • author.enabled
  • author.trust_level_changed
Queue item5
  • queue_item.created
  • queue_item.allowed
  • queue_item.rejectedsent 1
  • queue_item.actioned
  • queue_item.reopened
Action1
  • action.executedsent 2

The open item is resolved as Rejected, then the run is reported.

Sample data

Verify the webhook signature, then trust the body

The dashboard prints this function on each endpoint's page, ready to paste into a Node.js receiver.

What your endpoint receivesSample data
POST /hooks/infraredInfrared-Signature: t=1790863420,v1=<64 hex characters>Infrared-Event: action.executedInfrared-Delivery: whd_3k9pXa7Fm2Qw8Ls4Td6VbNInfrared-Api-Version: 2026-09-01{
  "id": "evt_2Vd8RkQ0mZx4TnL7sYc1Wb",
  "type": "action.executed",
  "created_at": "2026-10-01T14:03:40.000Z",
  "api_version": "2026-09-01",
  "data": {
    "action": { "key": "request_edits", … },
    "moderation_id": "mod_1B7lY4kzM9n2Qx8sRt3wVu",
    "author_id": null,
    "content_id": "listing_48213",
    "queue_item_id": "qi_0pX3hN9vKe6Ju2Ad5GwM8r",
    "value": { "reason": "Phone number in the description" },
    "source": "dashboard",
    "executed_at": "2026-10-01T14:03:40.000Z"
  }
}
Verify a deliveryNode.js
const crypto = require("crypto");
// Infrared-Signature: t=<unix>,v1=<hex>// v1 = HMAC-SHA256(secret, "<t>.<raw body>")function constructEvent(rawBody, header, secret, toleranceSeconds = 300) {  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));  const t = Number(parts.t);  if (!t || !parts.v1) throw new Error("malformed signature");  if (Math.abs(Date.now() / 1000 - t) > toleranceSeconds) {    throw new Error("signature too old");  }  const expected = crypto    .createHmac("sha256", secret)    .update(`${t}.${rawBody}`)    .digest("hex");  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))) {    throw new Error("bad signature");  }  return JSON.parse(rawBody);}

Every attempt is signed again with a new t. The whsec_ secret is shown once when the endpoint is created, and once more when you rotate it.

An endpoint that keeps failing is disabled

After 50 failed attempts in a row Infrared disables the endpoint and emails the organisation's owners and admins.

  • Eight attempts, then it stopsA 4xx response other than 408 or 429 ends the delivery at once.

  • Replay once it is enabledOn an active endpoint the same event goes out again, with eight attempts of its own.

POSThttps://example.com/hooks/infrared
Sample data
Event
moderation.completed
Infrared-Delivery
whd_9Qb4TnH6yWs1Kd8Xc3LmRv
Status
Pending
Response
503
Attempts
1 of 8
Retry schedule, drawn to scaleMinutes and seconds after the first attempt
  1. Attempt 1answered 5030:00first
  2. Attempt 2Next0:30+30 s
  3. Attempt 31:30+1 min
  4. Attempt 43:30+2 min
  5. Attempt 57:30+4 min
  6. Attempt 615:30+8 min
  7. Attempt 731:30+16 min
  8. Attempt 863:30+32 min
example.comActive43 consecutive failures

Every failed attempt counts, retries included. One 2xx response sets the count back to zero.

Sample data

Start moderating your users' content

Create an API key, send your first request and get a verdict back.