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

# Identify logged-in visitors

> Use signed identity tokens so TinyInbox can trust visitor details from your app.

Identity verification lets your backend tell TinyInbox who a logged-in visitor
is. Use it when you want the inbox to show trusted names, emails, or user IDs
instead of browser-provided details.

## When to use it

Use signed identity when:

* Visitors are logged in to your product
* You want replies tied to your own user IDs
* You need names or emails to come from your backend

For public marketing pages, browser identity is usually enough.

## Get the site secret

Create the site's first identity signing secret from the dashboard. TinyInbox
shows the new secret once, so copy it into your backend environment before
leaving the page.

You can also create or rotate it from the API:

```http theme={null}
POST /sites/{siteId}/security/rotate-identity-secret
Authorization: Bearer <user-session-token>
```

The response includes the new `identitySecret` once. Existing secrets are not
returned again.

## Sign the token

The identity token must:

* Use the `HS256` algorithm
* Use `typ: "JWT"` if your JWT library sets a type header. Tokens without
  `typ` are accepted, but any other `typ` value is rejected.
* Set `aud` to `tinyinbox-widget-identity`
* Include `userId` or `sub`
* Optionally include `name`, `email`, and `siteKey`
* Expire quickly

Example backend route:

```ts theme={null}
import { SignJWT } from "jose"

export async function GET() {
  const token = await new SignJWT({
    userId: user.id,
    name: user.name,
    email: user.email,
    siteKey: "tin_your_site_key",
  })
    .setProtectedHeader({ alg: "HS256", typ: "JWT" })
    .setAudience("tinyinbox-widget-identity")
    .setExpirationTime("30m")
    .sign(new TextEncoder().encode(process.env.TINYINBOX_IDENTITY_SECRET))

  return Response.json({ identityToken: token })
}
```

## Pass the token to the widget

Expose a browser-accessible endpoint that returns `{ identityToken }`, then pass
that token to the widget from client-only code. Keep using the widget script
from your TinyInbox dashboard; do not rebuild the widget URL or site key in
application code.

When you use signed identity for a logged-in visitor, do not also call
`TinyInbox.identify({ name, email })` for that same visitor. The signed token
should carry the trusted `name` and `email`; sending both can create an
anonymous session first and then switch to the signed visitor session.

For logged-in app pages, fetch the token once auth state is known and pass it to
TinyInbox. If you can run this before the copied widget script loads, the
command queues and the first widget session is already signed.

```js theme={null}
const { identityToken } = await fetch("/api/tinyinbox/identity").then((res) =>
  res.json()
)

window.TinyInbox = window.TinyInbox || []
window.TinyInbox.push(["identify", { identityToken }])
```

If you prefer an explicit ready hook, or the script is already installed before
auth state is known, pass the token after the runtime is ready:

```js theme={null}
const { identityToken } = await fetch("/api/tinyinbox/identity").then((res) =>
  res.json()
)

function onTinyInboxReady(callback) {
  if (window.TinyInbox && window.TinyInbox.isReady) {
    callback(window.TinyInbox)
    return
  }

  window.addEventListener(
    "tinyinbox:ready",
    (event) => callback(event.detail),
    { once: true }
  )
}

onTinyInboxReady((TinyInbox) => {
  TinyInbox.identify({ identityToken })
})
```

You can also render the token into the widget script with the
`data-tinyinbox-identity` attribute.

## Rotate the secret

Rotate a site's identity secret if it has been exposed or you need a fresh
secret. After rotation, update your backend environment variable.

API route:

```http theme={null}
POST /sites/{siteId}/security/rotate-identity-secret
Authorization: Bearer <user-session-token>
```

Existing identity tokens signed with the old secret stop working after rotation.
