InsionInsion
Webhooks

Events

Receive and verify Insion webhook events

Insion sends a webhook when a record's moderation status or a user's action status changes.

Webhook deliveries are not retried. Return a successful response only after you have safely accepted the event, and make your handler idempotent.

Event catalog

EventSent whenPayload
record.flaggedA record becomes flaggedRecord and, when available, its user
record.compliantA record becomes compliantRecord and, when available, its user
user.suspendedA user is suspendedUser
user.compliantA user returns to compliant statusUser
user.bannedA user is bannedUser

Request format

Insion sends an HTTPS POST request with these headers:

HeaderValue
Content-Typeapplication/json
X-SignatureHMAC-SHA256 signature of the exact request body

Any 2xx response is treated as a successful delivery. Other responses are treated as failed deliveries and are not retried.

Event envelope

Every event has the same outer shape. The top-level id identifies the moderation or user action that caused the event; use it together with event to make processing idempotent.

{
  "id": "mod_123",
  "event": "record.flagged",
  "timestamp": "1710000000000",
  "payload": {}
}
FieldTypeDescription
idstringThe moderation ID for record events, or user-action ID for user events.
eventstringOne of the event names listed above.
timestampstringUnix timestamp in milliseconds for the delivery.
payloadobjectThe record or user affected by the event.

Record events

record.flagged and record.compliant use a record payload. The nested user object is present only when the record is associated with a user.

{
  "id": "mod_123",
  "event": "record.flagged",
  "timestamp": "1710000000000",
  "payload": {
    "id": "record_123",
    "clientId": "post_123",
    "clientUrl": "https://example.com/posts/123",
    "name": "A post title",
    "entity": "post",
    "protected": false,
    "metadata": { "source": "community" },
    "status": "Flagged",
    "statusUpdatedAt": "1710000000000",
    "statusUpdatedVia": "AI",
    "user": {
      "id": "user_123",
      "clientId": "member_123",
      "protected": false,
      "status": "Suspended",
      "statusUpdatedAt": "1710000000000"
    }
  }
}
FieldDescription
payload.idInsion record ID.
payload.clientIdYour record identifier.
payload.clientUrlOriginal record URL, when provided.
payload.nameRecord name, when stored.
payload.entityRecord type, such as post or comment.
payload.protectedWhether the record is protected from automated moderation actions.
payload.metadataCustom record metadata, when provided.
payload.statusFlagged or Compliant.
payload.statusUpdatedAtUnix timestamp in milliseconds for the moderation result.
payload.statusUpdatedViaSource of the status change, such as AI, Manual, Automation, or a more specific automation reason.
payload.userAssociated user, when one exists. It contains id, clientId, optional clientUrl, protected, optional metadata, and optional status fields.

User events

user.suspended, user.compliant, and user.banned use a user payload.

{
  "id": "action_123",
  "event": "user.suspended",
  "timestamp": "1710000000000",
  "payload": {
    "id": "user_123",
    "clientId": "member_123",
    "clientUrl": "https://example.com/members/123",
    "protected": false,
    "metadata": { "plan": "pro" },
    "status": "Suspended",
    "statusUpdatedAt": "1710000000000",
    "statusUpdatedVia": "Automation"
  }
}
FieldDescription
payload.idInsion user ID.
payload.clientIdYour user identifier.
payload.clientUrlUser profile URL, when provided.
payload.protectedWhether the user is protected from automated actions.
payload.metadataCustom user metadata, when provided.
payload.statusSuspended, Compliant, or Banned.
payload.statusUpdatedAtUnix timestamp in milliseconds for the user action.
payload.statusUpdatedViaSource of the status change, such as AI, Manual, Automation, or a more specific automation reason.

Verify the signature

Verify X-Signature against the raw request body before parsing JSON. Use the webhook secret shown in the Developer section of your Insion dashboard.

import { createHmac, timingSafeEqual } from "node:crypto";

export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get("x-signature");
  const secret = process.env.INSION_WEBHOOK_SECRET;

  if (!signature || !secret) {
    return new Response("Unauthorized", { status: 401 });
  }

  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const valid =
    signature.length === expected.length &&
    timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!valid) {
    return new Response("Unauthorized", { status: 401 });
  }

  const event = JSON.parse(rawBody);
  // Store or process event.id + event.event before returning a 2xx response.

  return Response.json({ received: true });
}

Handling delivery safely

  • Deduplicate events with the combination of event and top-level id.
  • Store the event before performing slow work, then process it asynchronously.
  • Return a 2xx response promptly after accepting the event.
  • Keep the webhook secret private and verify every delivery.

On this page