Events
Events record what happened in a project: uploads and moderation decisions, manual reviews, processing failures, usage limits, new members and failing connectors. The events feed returns them oldest first after a cursor: call it without after to get a cursor for the current time, pass the returned cursor back to receive only newer events, and request again straight away while hasMore is true.
Guide: Webhooks & Connectors, which push the same events to you as they happen.
Feed and webhook envelopes
| Source | Shape |
|---|---|
| Events API | Event fields include severity, asset and facts directly on the event. |
| Webhook | The same fields are inside data; id, type, createdAt and projectId remain at the top level. |
Use the event ID to deduplicate deliveries. The asset snapshot records the decision when the event happened.
Endpoints
| Endpoint | Description |
|---|---|
GET / | List project events, oldest first, after a cursor. |
The event object
Asset events include the asset decision snapshot, and each event's facts is typed by its type. A webhook connector delivers the same event wrapped as { id, type, createdAt, projectId, data }, where data holds severity, title, description, assetId, asset and facts.
| Field | Type | Presence | Description |
|---|---|---|---|
id | string | Required | Unique event ID |
projectId | string | Required | ID of the project the event belongs to |
userId | string | Optional, nullable | ID of the user who caused the event, if any |
type | string | Required | Event type. See Event types. |
severity | "low" | "high" | Required | Default severity of the event type |
title | string | Required | Short human-readable summary |
description | string | Optional, nullable | Longer human-readable explanation |
assetId | string | Optional, nullable | ID of the asset the event is about, for asset events |
asset | object | Optional, nullable | Snapshot of the asset's moderation decision when the event was created, for asset events |
asset.id | string | Required | Asset ID |
asset.externalId | string | Optional, nullable | The asset's externalId, if you set one |
asset.status | "uploading" | "pending" | "approved" | "rejected" | "error" | Required | Asset status when the event was created |
asset.flagged | boolean | Optional, nullable | The report's flagged value; null until the asset has been classified |
asset.moderationAction | "ignore" | "alert" | "hide" | "purge" | Optional, nullable | Moderation action applied to the asset, if any |
facts | object[] | Required | Registered event facts. See Event facts. |
notify | boolean | Optional, nullable | Whether the event triggers in-app notifications |
createdAt | integer | Required | When the event was created, in milliseconds since the Unix epoch |
updatedAt | integer | Optional | When the event was last updated, in milliseconds since the Unix epoch |
readAt | number | Optional, nullable | When the event was marked as read in the portal, in milliseconds since the Unix epoch |
Event types
| Event | Category | Severity | Meaning |
|---|---|---|---|
| asset.uploaded | Assets | low | A new asset was uploaded to the project. |
| asset.classified | Assets | low | Moderation finished and the asset has a decision. |
| asset.flagged | Moderation | high | Automated moderation flagged the asset for a policy violation. |
| asset.approved | Moderation | low | A reviewer approved a rejected asset in the portal. |
| asset.rejected | Moderation | high | A reviewer rejected an asset in the portal. |
| asset.failed | Assets | high | The asset could not be processed and is in the error state. |
| usage.limit-reached | Usage | high | The project owner used their free upload allowance; new uploads are blocked. |
| project.member-joined | Team | low | Someone accepted an invitation and joined the project. |
| connector.failing | Connectors | high | Deliveries to a connector kept failing, so it was turned off. |
Event facts
| Event | Facts |
|---|---|
| asset.uploaded | asset-name: The asset's name when the event was created.asset-type: The asset's type, such as image or video.file-format: The uploaded file's format, such as jpg. |
| asset.classified | asset-name: The asset's name when the event was created.asset-type: The asset's type, such as image or video.status: The asset's status after classification: approved, rejected or error.moderation-action: The action applied to flagged content (alert, hide or purge), or null. |
| asset.flagged | asset-name: The asset's name when the event was created.asset-type: The asset's type, such as image or video.flagged-categories: The matched parent policy categories. Inspect the asset's moderation report for qualifier detail.moderation-action: The action applied to the flagged content (alert, hide or purge). |
| asset.approved | asset-name: The asset's name when the event was created.asset-type: The asset's type, such as image or video.reviewer-user-id: ID of the user who made the decision, or null when an API key made it.reviewer-name: Name or email of the user who made the decision, when known.note: The reviewer's note, or null. |
| asset.rejected | asset-name: The asset's name when the event was created.asset-type: The asset's type, such as image or video.reviewer-user-id: ID of the user who made the decision, or null when an API key made it.reviewer-name: Name or email of the user who made the decision, when known.note: The reviewer's reason for rejecting the asset, or null. |
| asset.failed | asset-name: The asset's name when the event was created.asset-type: The asset's type, such as image or video.previous-status: The asset's status before it failed, such as pending. |
| usage.limit-reached | limit: Number of free uploads in the allowance.used: Number of free uploads used.owner-user-id: ID of the project owner whose allowance was used. |
| project.member-joined | member-user-id: User ID of the new member.member-name: The new member's name or email.role: The member's role: admin, member, viewer or custom.invited-by-user-id: User ID of the person who sent the invitation. |
| connector.failing | connector-id: ID of the failing connector.connector-name: The connector's name.connector-type: The connector's type, such as webhook, slack or email.consecutive-failures: Number of deliveries in a row that failed after their retries.last-error: Summary of the most recent failure, or null. |
Endpoint details
List Events
List project events, oldest first, after a cursor. A webhook connector delivers each event as `{ id, type, createdAt, projectId, data }`, where `data` holds the remaining event fields. Each event's `facts` are `{ key, value, url? }` entries registered for its `type` (see the event types in the webhooks guide). Omit `after` to get a cursor for the current time without any events, then pass each response's `cursor` as `after` until `hasMore` is false. Polling receives new events without a public webhook URL, for example during local development.
import { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.events.list({
"limit": 50
});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchproject/not-foundevents/invalid-cursor