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

# In-app inbox & real-time updates

> Display a user's notification inbox, mark items as read, and stream new notifications to your UI in real time over a WebSocket subscription.

Every notification delivered through the `in_app` channel becomes an item in the recipient's **inbox**. This guide shows how to display that inbox, mark items as read, and stream new notifications to your UI in real time.

The inbox lives in the `archie_notifications_in_app` table, exposed through the auto-generated [GraphQL](/docs/features/backend/graphql-api-explorer/overview) operations on your project endpoint (`https://archie-core.services.archie.com/graphql`) with the usual `X-Project-Id`, `X-Environment`, and `Authorization` headers.

## What an inbox item contains

| Field                      | Meaning                                                |
| -------------------------- | ------------------------------------------------------ |
| `receiverId`               | The user the item belongs to.                          |
| `notificationType`         | The type it was sent from (its `eventKey`, `name`, …). |
| `notificationKey`          | The event key, stored on the item for convenience.     |
| `params`                   | The variables used to render it (your JSON payload).   |
| `actionUrl` / `actionText` | Optional call-to-action link and label.                |
| `isRead` / `readAt`        | Read state and timestamp.                              |
| `metadata`                 | Optional extra JSON you attached.                      |
| `expiresAt`                | When the item expires and should stop being shown.     |
| `createdAt`                | When it arrived.                                       |

## Show a user's inbox

Fetch the most recent, non-expired items for the signed-in user:

```graphql theme={null}
query Inbox {
  archieNotificationsInApp(
    filter: { receiverId: { equals: "2685ec12-a4c7-491d-a155-d0b09190993b" } }
    orderBy: { createdAt: DESC }
    first: 20
  ) {
    items {
      id
      notificationKey
      params
      actionUrl
      actionText
      isRead
      createdAt
      notificationType {
        name
      }
    }
  }
}
```

<Note>
  **Soft-deleted records.** List queries exclude soft-deleted rows by default, so a dismissed or removed inbox item won't reappear. To include records that have been soft-deleted, pass the `withDeleted: true` argument on the list query (e.g., `archieNotificationsInApp(withDeleted: true) { items { id } }`). Omit it — or set it to `false` — to return only active rows.
</Note>

### Count unread

Use a filter to show an unread badge:

```graphql theme={null}
query UnreadCount {
  archieNotificationsInApp(
    filter: {
      receiverId: { equals: "2685ec12-…" }
      isRead: { equals: false }
    }
  ) {
    count
  }
}
```

## Mark as read

```graphql theme={null}
mutation MarkRead {
  updateArchieNotificationsInApp(
    id: "item-id…"
    input: { isRead: true, readAt: "2026-06-24T15:00:00Z" }
  ) {
    id
    isRead
    readAt
  }
}
```

## Receive notifications in real time

To make new notifications appear instantly — without polling — use Archie's subscriptions. There are two steps: **register** a subscription configuration, then **connect** over a WebSocket to receive events.

### 1. Register the subscription

Use the `system { createSubscription }` mutation to declare which table and operations to watch. For an inbox, watch the in-app table for new rows (`CREATE`):

```graphql theme={null}
mutation CreateInboxSubscription($input: SubscriptionInput!) {
  system {
    createSubscription(input: $input) {
      id
      active
      name
    }
  }
}
```

**Variables**

```json theme={null}
{
  "input": {
    "name": "in_app_inbox",
    "description": "New in-app notifications",
    "active": true,
    "tables": [
      {
        "table": "archie_notifications_in_app",
        "operations": ["CREATE"],
        "fields": ["id", "receiver_id", "notification_key", "params", "action_url", "action_text", "is_read", "created_at"]
      }
    ]
  }
}
```

<Note>
  Inside a subscription's `fields` list, use the raw column names in `snake_case` (e.g., `receiver_id`, `notification_key`, `is_read`) — these are the database column names, not the camelCase API fields.
</Note>

### 2. Connect and listen

Open a WebSocket to the project's subscription endpoint and you'll receive an event each time a matching row is created:

```
wss://archie-core.archie-platform.com/subscriptions?project_id=<your-project-id>
```

When a new notification is delivered to a user, your client receives the row described by the `fields` you registered. Filter client-side by `receiver_id` so each user only reacts to their own items, then update the inbox UI (prepend the item, bump the unread badge).

<Tip>
  Watch `["CREATE", "UPDATE"]` if you also want live updates when an item is marked as read on another device.
</Tip>

## Putting it together

A complete in-app experience usually looks like this:

1. On load, **query** the inbox and the **unread count**.
2. **Register** (once) the `in_app_inbox` subscription and **connect** the WebSocket.
3. When an event arrives, **prepend** the new item and increment the badge.
4. When the user opens an item, **mark it read** and decrement the badge.

For email and other channels, no inbox handling is needed — Archie delivers those directly through the configured integration.
