Documentation
User authentication
Sign identity JWTs and control who can submit feedback or chat.
What it does
User Authentication lets your product prove who is logged in with a signed identity JWT. Your backend signs the token with your project secret key; Gatherly verifies it before treating someone as authenticated, storing their identity on feedback, or opening Chats.
Plain HTML attributes like data-auth-email are not proof of identity— anyone can change them in DevTools. Only a JWT signed with your secret key counts.
Configure it under Project → User Authentication. The Embed page updates the pasteable snippet to match your settings.
Secret key
Each project has a secret key starting with sk_live_. Copy it from User Authentication settings. Keep it on your server only — never put it in the embed script, front-end bundles, or public repos.
- Use the secret to sign identity JWTs on your backend after login.
- Regenerating the key immediately invalidates existing tokens until you mint new ones.
Identity JWT claims
Sign an HS256 JWT with your project secret. Claims:
| Claim | Required | Notes |
|---|---|---|
user_id | Yes | Stable id in your product. Stored as external_user_id. |
email | Yes | User email address. |
username | When data mode is email + username | Display name or handle. Omit when data mode is email only. |
image_url | No | Avatar URL (http/https). Updates the user's latest-known profile. |
plan | No | Free-form plan/tier label from your product (e.g. free, pro). |
stripe_customer_id | No | Your Stripe customer id so you can open the customer in the Stripe dashboard from Gatherly. |
exp | Yes | Expiry. Prefer ≤ 1 hour; Gatherly rejects tokens older than 24 hours from issue. |
Optional profile claims (image_url, plan, stripe_customer_id) are stored as the user's latest-known profile on each authenticated feedback, review, or chat request. Omitting a claim leaves the previous value unchanged.
Setup for your stack
Mint an identity JWT on a server you control, then pass it to the embed. Choose your platform for copy-paste examples. Each snippet is labeled Server (backend / template) or Client (browser). The secret key always stays as an environment variable — never in client code.
- Your server must mint the JWT and render it into the page — never sign with the secret in browser JavaScript.
- For static hosting only, skip verified auth or call a small backend that returns a token.
Mint on your server (Node)
import { SignJWT } from "jose";
const secret = new TextEncoder().encode(process.env.GATHERLY_SECRET_KEY);
const token = await new SignJWT({
user_id: user.id,
email: user.email,
username: user.username, // omit if email-only mode
image_url: user.avatarUrl, // optional
plan: user.plan, // optional
stripe_customer_id: user.stripeCustomerId, // optional
})
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("1h")
.sign(secret);Render the token (from your server template)
<script
src="https://YOUR_DOMAIN/embed.js"
data-key="pk_live_…"
data-auth-token="{{ identity_jwt }}"
data-auth-email="USER_EMAIL"
data-auth-username="USER_USERNAME"
async
></script>Copy your secret from Project → User Authentication into GATHERLY_SECRET_KEY on your server. After SPA login or logout without a full page reload, call Gatherly.invalidate() — see JavaScript API.
Who can give feedback
Audience controls who may submit. The API enforces this on every POST /api/feedback; the widget also hides the launcher when the visitor is not allowed. Authenticated means a valid identity JWT.
| Audience | Who can submit |
|---|---|
| All users | Anyone — with or without data-auth-token |
| Only authenticated | Visitors with a valid data-auth-token |
| Only unauthenticated | Visitors without data-auth-token |
Bug report screenshots also require authentication: only visitors with a valid identity JWT can attach images. Anonymous users can still submit text-only bugs when your audience allows it. See Feedback API for the upload flow.
Chats
The embed Chat tab is only shown when a token is present. Users can open multiple chats; your team replies from Dashboard → Chats. You can also start a chat from the dashboard (or via Users → Message) to reach early users without email. Chat APIs always require a verified JWT on the widget side — spoofing email in DevTools cannot list or send messages as another user.
How the widget decides
- On load, the embed fetches
/api/embed/config?key=…for your project’s enabled flag, audience, and data mode. - A visitor is treated as authenticated when auth is enabled and
data-auth-tokenis present. The API verifies the JWT with your secret on every request. - After config loads, if the audience does not allow that visitor, the launcher stays hidden.
- Allowed authenticated submissions include the token (body or
Authorization: Bearer). Identity fields are taken from verified claims, not from editable script attributes. - Auth attributes are read once at load. In an SPA, after login or logout update (or clear) the script attributes and call
Gatherly.invalidate()so the launcher shows or hides without a full refresh. See JavaScript API.