Skip to content
Early preview — you found Guuey before launch · official launch soon

Your identity endpoint

read as .md

If your app uses bring-your-own auth and is served on a custom domain, visitors sign in through an identity endpoint you host: a URL on your own site that recognizes the visitor’s existing session cookie and returns a short-lived Guuey token for them. The agent page never sees your credentials or your session — it only receives the token your endpoint chooses to mint.

This is the standalone-page counterpart of the identified embed’s token endpoint: the minting is identical, only the transport differs. It is one of two bring-your-own ways to sign visitors into the page — the other, a redirect to your own identity provider, works on every host (Guuey’s included) but costs a full-page hop; this one is silent where your session cookie is reachable. An embed lives inside your page and can call your backend directly; a standalone page on chat.example.com is its own origin, so the browser fetches your endpoint cross-origin with credentials — which is why the CORS contract below is exact.

  1. A visitor opens your agent on your custom domain, already signed in to your site in the same browser.

  2. The page requests your identity endpoint — a plain GET with credentials: "include" and Accept: application/json. The visitor’s session cookie is the only credential.

  3. Your endpoint authenticates the cookie, mints a token for that user with @guuey/widget-auth, and responds 200 with:

    { "token": "<the signed token>" }
  4. The page uses that token for the chat. Conversation history, memory, and profile all key off the identity your endpoint asserted — the same identity every other Guuey surface derives from that token; how that works is on The identity model.

There is no guest fallback on this path: if your endpoint can’t be reached or answers anything but the contract above, the page shows a clear “sign-in unavailable” notice rather than silently dropping your visitor into an anonymous session. When a token expires, the page silently re-queries your endpoint — the visitor’s session cookie is still in the browser, so renewal is just another fetch, no prompt. Only if that fetch fails does the visitor see the “sign-in service didn’t respond” notice with a Try again button.

The endpoint is part of the app’s standalone page settings (it only ever runs on the agent’s own page). Set it in the console under Settings → General → Standalone page, or:

Terminal window
guuey apps update <appId> --identity-endpoint-url https://www.example.com/api/guuey-identity

The URL must be https://. It is stored on the app whatever the auth mode, but only used when the app’s authentication is bring-your-own.

The same minting code as an identified embed’s token endpoint — enrol the app once with guuey widget keys create, then:

import { signUserToken } from "@guuey/widget-auth";
// Pseudocode for any Node backend — authenticate YOUR session first.
export async function handleGuueyIdentity(req, res) {
const session = await readYourSession(req); // your cookie, your rules
if (!session) return res.status(401).end();
const { token } = await signUserToken(
{ userId: session.userId, name: session.name, email: session.email },
{
appId: process.env.GUUEY_APP_ID!,
appSecret: process.env.GUUEY_APP_SECRET!,
// The API origin, without `/v1` — or set GUUEY_API_URL.
apiBaseUrl: "https://api.us-east-1.guuey.com",
}
);
res.setHeader("Content-Type", "application/json");
res.json({ token });
}

The request arrives cross-origin with credentials, so the browser holds your response to the strictest CORS rules. Your endpoint must send all three headers, exactly:

Header Value Why
Access-Control-Allow-Credentials true Without it the browser discards the response of a credentialed request.
Access-Control-Allow-Origin The exact requesting origin, e.g. https://chat.example.com — only after you’ve validated it * is rejected outright by browsers when credentials are involved, and reflecting any Origin unvalidated turns your session cookie into a token mint for every site on the internet. Validate against the fixed list of hostnames you serve your agent on, and refuse the rest.
Vary: Origin You’re returning a per-origin header; without Vary a shared cache can serve one origin’s Allow-Origin to another and break sign-in intermittently.
const ALLOWED_ORIGINS = new Set([
"https://chat.example.com", // your custom domain(s), nothing else
]);
const origin = req.headers.origin;
if (origin && ALLOWED_ORIGINS.has(origin)) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
}
res.setHeader("Vary", "Origin");

The request is a simple GET (no custom headers), so no preflight OPTIONS handling is required. If you route it through middleware that adds one anyway, answer the preflight with the same three headers.

A CORS refusal is deliberately invisible to the page — the browser reports it only as a network failure — so if sign-in shows as unavailable, check your endpoint’s response headers in the browser’s network inspector first.

  • If your agent’s custom domain shares a site with your session — say the page is chat.example.com and the endpoint www.example.com — the request is same-site, and default (SameSite=Lax) session cookies are sent. Most setups need no cookie changes.
  • If they are different sites entirely, the cookie must be issued with SameSite=None; Secure to accompany the request — with all the caveats that setting carries. Keeping the agent’s domain under the same site as your auth is the simpler path.