Skip to main content
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:
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:
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.
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:
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:
POST /sites/{siteId}/security/rotate-identity-secret
Authorization: Bearer <user-session-token>
Existing identity tokens signed with the old secret stop working after rotation.