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
| Event | Sent when | Payload |
|---|---|---|
record.flagged | A record becomes flagged | Record and, when available, its user |
record.compliant | A record becomes compliant | Record and, when available, its user |
user.suspended | A user is suspended | User |
user.compliant | A user returns to compliant status | User |
user.banned | A user is banned | User |
Request format
Insion sends an HTTPS POST request with these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Signature | HMAC-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": {}
}| Field | Type | Description |
|---|---|---|
id | string | The moderation ID for record events, or user-action ID for user events. |
event | string | One of the event names listed above. |
timestamp | string | Unix timestamp in milliseconds for the delivery. |
payload | object | The 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"
}
}
}| Field | Description |
|---|---|
payload.id | Insion record ID. |
payload.clientId | Your record identifier. |
payload.clientUrl | Original record URL, when provided. |
payload.name | Record name, when stored. |
payload.entity | Record type, such as post or comment. |
payload.protected | Whether the record is protected from automated moderation actions. |
payload.metadata | Custom record metadata, when provided. |
payload.status | Flagged or Compliant. |
payload.statusUpdatedAt | Unix timestamp in milliseconds for the moderation result. |
payload.statusUpdatedVia | Source of the status change, such as AI, Manual, Automation, or a more specific automation reason. |
payload.user | Associated 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"
}
}| Field | Description |
|---|---|
payload.id | Insion user ID. |
payload.clientId | Your user identifier. |
payload.clientUrl | User profile URL, when provided. |
payload.protected | Whether the user is protected from automated actions. |
payload.metadata | Custom user metadata, when provided. |
payload.status | Suspended, Compliant, or Banned. |
payload.statusUpdatedAt | Unix timestamp in milliseconds for the user action. |
payload.statusUpdatedVia | Source 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
eventand top-levelid. - Store the event before performing slow work, then process it asynchronously.
- Return a
2xxresponse promptly after accepting the event. - Keep the webhook secret private and verify every delivery.