Keys and tokens
Your organization has one Signal app. Every website and mobile app uses its publishable key and a short-lived JWT, which your backend signs or your sign-in provider issues. Hinto resolves the organization and the Signal app from the publishable key, and in a browser it answers that lookup only on a website from the list in Settings > Hinto Signal. A mobile app sends no origin. The backend values (HINTO_SIGNAL_APP_ID, HINTO_SIGNAL_KID, and the signing secret HINTO_SIGNAL_SECRET) stay on your authenticated backend. Hinto shows the signing secret only when the app is created (by Set up without a coding agent in Settings > Hinto Signal, by the first provision_hinto_signal call, or by the pairing claim that creates the app), after Roll secret, or after a provision_hinto_signal call with reissueCredentials. Copy it to protected server configuration before closing the reveal. Ordinary settings reads never return it. If the value is lost, choose Roll secret. Tokens signed with the old secret are accepted only during the retiring window of 12 hours plus 60 seconds.
The publishable key is public by design. Roll key replaces it, and the old key keeps working for 7 days so builds you already shipped keep working; Stop it now ends it early. A secret key (hinto_sk_…, from Settings > API keys) is for servers and coding agents only, and Hinto refuses it from a website or an app. Pause Signal stops customer requests immediately.
When your backend signs the token, it derives sub from its authenticated session. A client-supplied customer ID must never decide token identity. Tokens use HS256, the active signing key ID (HINTO_SIGNAL_KID) in kid, the Signal app ID (HINTO_SIGNAL_APP_ID) in aud, and iat/exp with lifetime at most 12 hours. Return the JWT with Cache-Control: no-store, keep it in memory, and mint a new token after sign-in or account switch. Sign-out clears the runtime before the next account paints.
The token can also carry optional email, name, and company string claims. Your backend reads them from the same server session or user record as sub, never from the request. Hinto trusts them for two things only. It matches the signed email to an existing customer in the same organization, so the ticket arrives with that customer as the requester. Otherwise it fills the claims into the ticket’s Keep form for an operator to confirm. Hinto never creates a customer from claims on its own. It ignores a blank or non-string claim, and an email that is not shaped like an email address.
Sign-in provider tokens
An organization can name the sign-in provider its apps already use (Supabase, Firebase, Clerk, Auth0, or another OpenID provider), so an app without a backend passes that provider’s token instead of one your backend signs. An organization admin names it, by approving a pairing that carries it, with the MCP, or in Settings > Hinto Signal. A Signal app trusts one provider. A request to trust a different one is refused withidentity_provider_conflict until an admin changes it in Settings, and Remove stops Hinto accepting the provider’s tokens at once.
Hinto checks every provider token:
- Asymmetric signatures only. The token is signed with RS256, RS384, RS512, PS256, ES256, ES384, or EdDSA. HS256 is reserved for tokens signed with your Signal signing secret, so a provider token is never checked against that secret, and an HS256 token never reaches the provider check.
- Issuer and audience pinned.
issmust equal the issuer derived from what the admin entered, andaudmust contain the provider’s audience:authenticatedfor Supabase, the project ID for Firebase, and the entered audience for Auth0 and other OpenID providers. Clerk session tokens carry no audience, so Hinto checks only their issuer. Another OpenID provider must have an audience, because one issuer can sign tokens for many apps. - Keys fetched over public HTTPS. Hinto reads the provider’s public keys (JWKS) from the provider’s own HTTPS address, never a private or local network address, with no redirects, a timeout, and a size limit. For another OpenID provider it reads
<issuer>/.well-known/openid-configurationonce, when the provider is saved, and refuses a document that names a different issuer. Keys are cached for about 10 minutes. A token signed with a key the cache does not hold refetches the keys at most once a minute, which is how a provider’s key rotation is picked up. - Times and subject. The token needs
sub,iat, andexp, may live at most 12 hours, and is checked with 60 seconds of clock tolerance.subis the customer’s identity, and the SDK refuses a token whosesubis not thesubjectthe app passed. - Verified email only. Hinto takes
emailonly when the token carries a top-levelemail_verified: trueclaim. It never reads verification fromuser_metadata, because Supabase lets the signed-in user write that object, so a Supabase token gives no email. The name comes fromname,user_metadata.full_name, oruser_metadata.name. No company is taken from a provider token. These claims are used the same way as claims your backend signs: to match an existing customer or fill the Keep form, never to create a customer on their own.
Exact origins and sessions
List each website’s complete scheme, host, and port. A coding agent’s pairing adds the origins it sends; without an agent, an admin chooses Add website in Settings > Hinto Signal. Wildcards, paths, credentials, and mixed-case origins are rejected. The list holds up to 10 websites. One list gates both support requests and session capture from a browser. A new website can take up to one minute to pass the session capture cache. The customer ID in the token must match the captureexternal_id identity for session attachment.
A conversation belongs to the authenticated subject and organization. Hinto returns customer messages and successfully delivered human replies in its customer transcript. Internal notes, pending or failed replies, and legacy widget records do not appear there. A claimed historical widget delivery is cancelled without a network send. If your product records sensitive fields, configure capture privacy controls before initializing the SDK; see privacy controls.
Browser and server boundaries
The root browser SDK and IIFE contain capture only. Import the headless runtime from@gethinto/browser-sdk/conversation and styled React components from @gethinto/native-support-react. CSS is compiled and has no runtime injection. Server-side rendering may import the component package, but the runtime must be created only in a browser. Content Security Policy must permit your API and your token endpoint, or your sign-in provider when it supplies the token. The release has no public token mint endpoint; the lab’s synthetic login runs only on loopback and is not production authentication.