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

# Data model

> The objects you work with and how they relate.

```
App (yours)
 └── User          one per (appId, userRef)
      └── Connection   one per connected provider account
           └── Conversation   one per provider conversation
                └── Message
```

## App

Your application, identified by `appId`. It holds your app secret, your API keys, your webhook URL and secret, and the branding of the hosted pages. Everything below is scoped to one app: the same person connecting through two apps produces two unrelated users, and neither app can see the other's data.

## User

Created the first time a `userRef` opens a connect link for your app. `userRef` is your identifier, any string you choose; Verso never interprets it. It is the key of `GET /api/connections` and appears in every webhook payload.

## Connection

A connected provider account of a user.

| Field | Description |
| - | - |
| `id` | UUID. Changes when the user reconnects. |
| `provider` | `chatgpt` |
| `status` | `active`, `needs_reauth` or `revoked` |
| `capturedAt` | When the user completed the connect flow |
| `lastSyncAt` | Last successful sync, i.e. the last time the session was verified; `null` before the first one |
| `conversationCount` | Conversations currently imported |
| `recentConversationCount` | Conversations whose first message is within the last 30 days |

Lifecycle:

```
connect  ──▶  active  ──(session rejected)──▶  needs_reauth  ──(user reconnects)──▶  revoked, replaced by a new active connection
                 │
                 └──(user or partner deletes)──▶  revoked, credentials destroyed
```

A user may have several connections at once: two different ChatGPT accounts, or an old `revoked` one next to its replacement. `GET /api/connections` lists them all.

## Conversation

A conversation of the provider account, imported and normalized.

| Field | Description |
| - | - |
| `id` | Verso's UUID |
| `external_id` | The provider's conversation id |
| `title` | The provider's title, may be `null` |
| `update_time` | Last change on the provider side; drives the ordering of `GET /api/conversations` |
| `payload.messages` | The current branch, chronological, without system messages |

A conversation is updated in place when the provider reports a newer `update_time`; it keeps the same `id`.

## Message

| Field | Description |
| - | - |
| `role` | `user`, `assistant` or `tool` |
| `content` | Text, possibly empty for attachment-only messages |
| `timestamp` | When it was written, ISO 8601 or `null` |
| `id`, `contentType`, `status`, `endTurn`, `model` | Provider metadata when available |
| `attachments`, `contentReferences` | Files and citations, metadata only |

The first message's `timestamp` is what `recentConversationCount` is computed from.

## What is not exposed

Session credentials are never returned by any endpoint, not even encrypted. The provider's raw conversation objects are kept by Verso to re-normalize data when the message format improves, but are not part of the API.
