# Sessions

> Start the short-lived tokens the embedded editor and presence settings run on.

A session is a short-lived token that lets one of your users into one document in the [embedded editor](/reference/embed/), or into presence settings. Your server starts it with your secret key and hands the token to your page; the page passes it to Proof and never sees your key. Tokens last 15 minutes, and the embed asks your `getToken` function for a new one before the old one runs out, so an open editor keeps working for as long as the person is allowed in. See [Embed the editor](/guides/embed-the-editor/).

A session token only opens the editor or presence settings. It can't call the REST API.

## The session object

| Attribute | Type | Description |
| --- | --- | --- |
| `token` | string | The token to pass to the page, starting `est_`. |
| `user` | string | The user's Proof id. |
| `document` | string or null | The document it opens, or `null` for a presence session. |
| `purpose` | string | `editor` or `presence`. |
| `role` | string or null | The role the user has in the document for this session: `owner`, `editor`, `suggester` or `viewer`. `null` for a presence session. |
| `via` | string or null | `member` when the role comes from membership, `link` when it comes from the document's link access. `null` for a presence session. |
| `expires_at` | timestamp | When the token stops being accepted, 15 minutes after it was started. |

```json
{
  "token": "est_q7Lr2VkX9pN4sT8wY3mB6cZ1",
  "user": "usr_5Kq2Lm8R",
  "document": "doc_8Hq2Lm4R",
  "purpose": "editor",
  "role": "owner",
  "via": "member",
  "expires_at": "2026-10-30T18:35:00Z"
}
```

## Create a session

`POST /v1/sessions`

Starts a session for the editor, or for presence settings when you pass `purpose: "presence"`.

| Parameter | Type | Description |
| --- | --- | --- |
| `user` | string, required | The user's Proof id. The user must exist. |
| `document` | string | The document to open. Required unless `purpose` is `presence`. |
| `via` | string | `link` to let in someone who isn't a member, with the document's link access as their role. Leave it out for members. |
| `purpose` | string | `editor` (the default) or `presence`. |
| `group` | string | Up to 200 characters of yours that the session belongs to, such as the id of the person's own sign-in session with you, so you can [end them together](#end-a-users-sessions). |

### For the editor

Proof works out the user's role when it starts the session: their member role, or with `via: "link"`, the document's `link_access`. A member who comes by link keeps whichever role is higher. The role is checked again on every edit, so lowering it or removing the member takes effect in an open editor at once, whatever the token says.

<Tabs syncKey="lang">
<TabItem label="curl">

```bash
curl https://api.proof.exalto.ai/v1/sessions \
  -H "Authorization: Bearer $PROOF_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user": "usr_5Kq2Lm8R", "document": "doc_8Hq2Lm4R"}'
```

</TabItem>
<TabItem label="Node">

```ts
const session = await proof.sessions.create({
  user: "usr_5Kq2Lm8R",
  document: "doc_8Hq2Lm4R",
});
```

</TabItem>
</Tabs>

```json
{
  "token": "est_q7Lr2VkX9pN4sT8wY3mB6cZ1",
  "user": "usr_5Kq2Lm8R",
  "document": "doc_8Hq2Lm4R",
  "purpose": "editor",
  "role": "owner",
  "via": "member",
  "expires_at": "2026-10-30T18:35:00Z"
}
```

For someone who followed a shared link:

<Tabs syncKey="lang">
<TabItem label="curl">

```bash
curl https://api.proof.exalto.ai/v1/sessions \
  -H "Authorization: Bearer $PROOF_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user": "usr_9Tb3Wc1X", "document": "doc_8Hq2Lm4R", "via": "link"}'
```

</TabItem>
<TabItem label="Node">

```ts
const session = await proof.sessions.create({
  user: "usr_9Tb3Wc1X",
  document: "doc_8Hq2Lm4R",
  via: "link",
});
```

</TabItem>
</Tabs>

```json
{
  "token": "est_d2Hs9QmW4kR7xV1nJ6pL8tF3",
  "user": "usr_9Tb3Wc1X",
  "document": "doc_8Hq2Lm4R",
  "purpose": "editor",
  "role": "suggester",
  "via": "link",
  "expires_at": "2026-10-30T18:35:00Z"
}
```

A document in the trash still opens, only to read: for its owners when it's in the trash for everyone, and for a member who put it in their own. The session's `role` is then `viewer`.

People who come by link aren't added as members. Their writing is credited to them like anyone else's, and they appear in the document's mention list while they have it open.

### For presence settings

A presence session opens the window where a user adds security keys and chooses how often to check in. It isn't tied to a document. Pass its token to `mountPresenceSettings` or `openPresenceSettings` in the [embed API](/reference/embed/#presence-settings). See [Set up check-ins](/guides/set-up-check-ins/).

<Tabs syncKey="lang">
<TabItem label="curl">

```bash
curl https://api.proof.exalto.ai/v1/sessions \
  -H "Authorization: Bearer $PROOF_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user": "usr_5Kq2Lm8R", "purpose": "presence"}'
```

</TabItem>
<TabItem label="Node">

```ts
const session = await proof.sessions.create({
  user: "usr_5Kq2Lm8R",
  purpose: "presence",
});
```

</TabItem>
</Tabs>

```json
{
  "token": "est_m5Tc8Wq1Zr6Kv3Np9Ys2Bx7H",
  "user": "usr_5Kq2Lm8R",
  "document": null,
  "purpose": "presence",
  "role": null,
  "via": null,
  "expires_at": "2026-10-30T18:35:00Z"
}
```

### Errors

| Error | When |
| --- | --- |
| `400 missing_parameter` on `document` | `purpose` is `editor` and there's no `document`. |
| `400 invalid_parameter` on `group` | Empty, or longer than 200 characters. |
| `403 no_access` | The user isn't a member, and either you didn't pass `via: "link"` or the document's link access is `restricted`. |
| `404 user_not_found` | The user doesn't exist. |
| `404 document_not_found` | The document doesn't exist. |
| `409 document_trashed` | The document is in its owners' trash, and the user isn't one of its owners. |

## End a user's sessions

`DELETE /v1/users/{user_id}/sessions`

Ends a user's sessions at once: all of them, or with `group`, only those started in that group. Call it when someone signs out of your product, or is signed out everywhere. Their open editors lose their connection within moments and ask your page for a new token, which your server refuses once they're signed out.

| Parameter | Type | Description |
| --- | --- | --- |
| `user_id` | string, path, required | The user's Proof id. |
| `group` | string, query | Only the sessions started with this `group`. |

<Tabs syncKey="lang">
<TabItem label="curl">

```bash
curl "https://api.proof.exalto.ai/v1/users/usr_5Kq2Lm8R/sessions?group=sess_42" \
  -X DELETE \
  -H "Authorization: Bearer $PROOF_SECRET_KEY"
```

</TabItem>
<TabItem label="Node">

```ts
await proof.users.endSessions("usr_5Kq2Lm8R", { group: "sess_42" });
```

</TabItem>
</Tabs>

```json
{ "deleted": 2 }
```

:::caution
A session token lets whoever holds it write as that user until it expires. Send it only to the person it's for, over HTTPS, and don't log it. Start sessions only after your own sign-in has checked who is asking.
:::