> ## Documentation Index
> Fetch the complete documentation index at: https://developers.reintersect.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Event catalog

> Every lifecycle event, the scope it needs, and its payload.

> Reintersect apps are in developer preview. Names and limits can still change before general
> release.

Each section lists the event's `data`. In an SDK handler these fields arrive at the top level of the
context, next to `reintersect`, `actor`, `env`, `config` and `waitUntil`. Literal values such as
`origin` are open: new values can appear, so handle unknown ones.

| Event | Read scope | When |
| - | - | - |
| [`app.installed`](#app-installed) | none | The app is installed or reinstalled in a workspace. |
| [`app.uninstalled`](#app-uninstalled) | none | The app is uninstalled or its install revoked. |
| [`app.reconsentRequired`](#app-reconsentrequired) | none | A new version needs more access and waits for an admin. |
| [`block.created`](#block-created) | none | One of your blocks is inserted, cloned or placed by a member from an agent. |
| [`block.removed`](#block-removed) | none | One of your blocks is deleted. |
| [`reference.created`](#reference-created-and-reference-removed) | `conversations:read` | A link to one of your objects is added in a circle your app can reach. |
| [`reference.removed`](#reference-created-and-reference-removed) | `conversations:read` | A link to one of your objects is deleted in a circle your app can reach. |
| [`decision.published`](#decision-published-and-decision-updated) | `decisions:read` | A decision is published in a circle your app can reach. |
| [`decision.updated`](#decision-published-and-decision-updated) | `decisions:read` | A published decision changes. |
| [`call.recapReady`](#call-recapready) | `calls:read` | A call's recap is ready. |
| [`post.created`](#post-created) | `conversations:read` | A conversation starts in a circle your app can reach. |
| [`reply.created`](#reply-created) | `conversations:read` | Someone replies in a circle your app can reach. |
| [`app.mentioned`](#app-mentioned) | `conversations:read` | A member mentions your app. |
| [`message.filed`](#message-filed) | `conversations:read` | A chat message is filed into a conversation, moved to another one, or unfiled. |
| [`conversation.updated`](#conversation-updated) | `conversations:read` | A conversation's title, line, status or Waiting on changes. |
| [`conversation.agreed`](#conversation-agreed) | `conversations:read` | Something is agreed in a conversation. |

Text fields such as titles are at most 300 characters, and bodies (`summary`, `recap`, `text`) at
most 4,000.

## Shared shapes

```ts theme={null}
interface EventAuthor {
  id: string;
  kind: "member" | "app" | "imported" | "agent" | "external";
  memberId: string | null;
  displayName: string;
}
```

Location fields `circleId`, `conversationId` and `replyId` are `null` when they don't apply:
`replyId` is `null` for an object in the conversation's opening post, and `conversationId` is `null`
for a chat message that isn't filed in a conversation. When Reintersect files a chat message into a
conversation later, or someone moves it, [`message.filed`](#message-filed) tells you where it went.

## app.installed

```ts theme={null}
{ scopes: Scope[]; config: Record<string, Json>; version: number }
```

`version` is the installed app version number. `config` is the install config the admin filled in.

## app.uninstalled

```ts theme={null}
{ reason: "uninstalled" | "revoked" }
```

## app.reconsentRequired

```ts theme={null}
{
  currentVersion: number;
  pendingVersion: number;
  addedScopes: Scope[];
  addedHosts: string[];
  addedEvents: EventName[];
  widened: AccessCategory[];
}
```

`widened` lists every kind of access the new version adds: `"scopes"`, `"egress_hosts"`,
`"domains"`, `"events"`, `"mcp"`, `"actions"` or `"message_actions"`. It can include kinds the
`added*` lists don't cover, such as a new MCP server or new actions. New categories may appear, so
treat unknown values as "more access".

The install stays on `currentVersion` until an admin approves. See
[Publishing new versions](/concepts/install-and-consent#publishing-new-versions).

## block.created

```ts theme={null}
{
  block: {
    id: string;
    type: string;
    props: Record<string, Json>;
    state: Record<string, Json>;
    version: number;
    circleId: string | null;
    conversationId: string | null;
    replyId: string | null;
  };
  origin: "insert" | "clone" | "agent";
}
```

`clone` means a member pasted a copy of an existing block; it starts with fresh state.

## block.removed

```ts theme={null}
{ block: { id: string; type: string; version: number } }
```

## reference.created and reference.removed

```ts theme={null}
{
  reference: {
    kind: string;
    externalId: string;
    url: string;
    version: number;
    circleId: string | null;
    conversationId: string | null;
    replyId: string | null;
  };
}
```

One event per occurrence: the same object linked in two replies produces two events. Like the
conversation events, these need `conversations:read` and only arrive from circles your app can
[reach](/events/overview#which-installs-receive-an-event), because the occurrence tells you where a
member linked your object.

## decision.published and decision.updated

```ts theme={null}
{
  decision: {
    id: string;
    conversationId: string;
    circleId: string;
    circleName: string;
    title: string;
    summary: string;
    url: string;
    version: number;
    publishedAt: string;
  };
}
```

## call.recapReady

```ts theme={null}
{
  call: {
    id: string;
    conversationId: string;
    circleId: string;
    circleName: string;
    title: string;
    recap: string;
    url: string;
    version: number;
    startedAt: string | null;
    endedAt: string | null;
  };
}
```

## post.created

```ts theme={null}
{
  post: {
    id: string;
    title: string;
    circleId: string;
    circleName: string;
    text: string;
    author: EventAuthor;
    url: string;
    version: number;
    createdAt: string;
  };
}
```

## reply.created

```ts theme={null}
{
  reply: {
    id: string;
    conversationId: string | null;
    conversationTitle: string | null;
    parentReplyId: string | null;
    circleId: string;
    circleName: string;
    text: string;
    author: EventAuthor;
    url: string;
    version: number;
    createdAt: string;
  };
}
```

## app.mentioned

```ts theme={null}
{
  message: {
    id: string;
    conversationId: string | null;
    replyId: string | null;
    circleId: string;
    circleName: string;
    text: string;
    author: EventAuthor;
    url: string;
    version: number;
    createdAt: string;
  };
}
```

In circles that use chat, `reply.created` and `app.mentioned` also cover chat messages. A chat message
not filed in a conversation has `conversationId` (and, for `reply.created`, `conversationTitle`) set
to `null`; answer it with `replyTo` or `circleId`. See [Writing to circles](/writing-to-circles).

Mentions by members also [invite](/concepts/install-and-consent#reach) your app to that circle.

## message.filed

```ts theme={null}
{
  messageId: string;
  circleId: string;
  conversationId: string | null;
  previousConversationId: string | null;
  conversationTitle: string | null;
  filing: "machine" | "explicit" | "corrected";
}
```

In circles that use chat, Reintersect files messages into conversations as people talk, and members
can move, merge, split and unfile them. You get one event per message each time its conversation
changes: `previousConversationId` is `null` when the message wasn't filed before, and
`conversationId` (with `conversationTitle`) is `null` when it was unfiled. `filing` says who placed it:
`machine` for Reintersect, `corrected` for a member's correction, `explicit` for a message sent
straight into a conversation. A message staying where it was sends nothing, and your app never hears
about its own messages.

## conversation.updated

```ts theme={null}
{
  conversationId: string;
  circleId: string;
  title: string;
  line: string | null;
  status: {
    phase: "discussing" | "waiting" | "agreed" | "decided" | "doing" | "done" | "quiet";
    label: string | null;
  };
  waitingOn: { memberId: string; name: string }[];
  changed: ("title" | "line" | "status" | "waitingOn")[];
  source: "machine" | "person";
  revision: number;
}
```

A chat conversation's card: its title, `line` (where it stands), status and the people it's waiting
on. `title` is empty until the conversation has one. `label` is the circle's own name for the status,
or `null` when the conversation shows the plain phase.

Reintersect refreshes cards often, so it gathers changes for about 30 seconds and sends one event with
the latest card. `changed` lists what differs from the card as it was before those changes. Nothing is
sent when no field you can see changed. `source` is `person` when a member set one of the changed
fields, such as a status.

`revision` is the card's revision, which only goes up. Deliveries can arrive out of order, so keep the
highest `revision` you have seen for each conversation and ignore a delivery with a lower one. Still
apply a delivery with the same revision, because a member's change, such as setting a status, keeps
the revision.

## conversation.agreed

```ts theme={null}
{
  conversationId: string;
  circleId: string;
  statement: string;
  sourceMessageIds: string[];
  source: "machine" | "person";
  revision: number;
}
```

Reintersect noticed that people agreed on something (`machine`), or a member confirmed the agreement
(`person`). `statement` is what was agreed and `sourceMessageIds` are the messages behind it: the
proposal first, then the replies that backed it. `sourceMessageIds` can be empty for an agreement
made on a call. `revision` is the card's revision when the agreement was recorded, the same counter as
`conversation.updated`: ignore an agreement older than the last revision you saw for that
conversation. Dismissing an agreement sends nothing.

`message.filed`, `conversation.updated` and `conversation.agreed` exist only in circles that use chat,
not in circles set to posts.

## The envelope

Without the SDK, each event arrives as a signed `POST` with this body. See the
[delivery protocol](/reference/delivery-protocol).

```json theme={null}
{
  "id": "msg_2mT0Kq7Y",
  "type": "decision.published",
  "timestamp": "2026-10-05T09:00:00.000Z",
  "app": { "id": "app_123", "slug": "changelog", "version": 4 },
  "install": { "id": "inst_123", "workspaceId": "org_123", "kind": "workspace" },
  "installToken": { "token": "reiapp_…", "expiresAt": "2026-10-05T10:00:00.000Z" },
  "actor": { "memberId": "mem_123", "name": "Ada Lovelace" },
  "locale": "en",
  "timeZone": "UTC",
  "data": { "decision": { "id": "dec_1" } }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.