Player accounts and login
Optional email/password login on top of anonymous-first, so a player keeps one identity across devices.
Anonymous-first is the default and it is enough for most games: every device gets a player automatically. Player accounts add a DURABLE identity on top, so a player can sign in and reach the SAME player from any device. It is opt-in per game and never a wall: anonymous play keeps working, and a player only sees a login if you show one. Enable it in the dashboard (Setup → Keys → Player accounts); until you do, the SDK's tg.auth calls report no providers.
How it fits: a credential layer, not a new data model
The account only proves who the player is. Your game data (saves, inventory, stats) still lives under the same per-game player, reached by the same player token every SDK call already uses. Signing in just tells Triggair which player this device is. Accounts are per game: the same login gives a player their Sable Cipher player in Sable Cipher and their Verge Rush player in Verge Rush, and the two never see each other's data.
await tg.auth.providers(); // ['password'] when enabled, [] when off
const { needsConfirmation } = await tg.auth.signUp(email, password); // sends a confirmation email
const r = await tg.auth.signInWithPassword(email, password); First sign-in links the current anonymous player
When a player who has been playing anonymously signs in for the first time, their current progress is LINKED to the new account (outcome: 'linked') — nothing is lost, no copy happens. On another device, signing in returns that same player (outcome: 'resumed' or 'adopted'). A brand-new sign-in with no local progress just creates the account's player (outcome: 'created').
The one conflict, and how the merge resolves it
There is exactly one case that needs a decision: the account already has progress AND the current device has its own anonymous progress. Then outcome is 'conflict' and the response carries a merge choice. keep_account keeps the account's data; use_anonymous replaces it with THIS device's progress. Either way the losing player row is PARKED, never deleted, so support can restore it. Show the player the choice; do not silently discard.
const r = await tg.auth.signInWithPassword(email, password);
if (r.outcome === 'conflict') {
// r.merge tells you both players exist; ask the player which to keep
await tg.auth.resolveMerge('keep_account'); // or 'use_anonymous'
}
tg.auth.onIdentityChanged(() => refetchEverything()); // the active player id may have changed Sign out, reset, and what to build
signOut() clears the session and rotates the device to a FRESH anonymous player (so a shared device does not leave the account reachable). sendPasswordReset(email) emails a reset link. All you build is a form: collect an email + password, call signUp / signInWithPassword, and handle the merge prompt on conflict.
await tg.auth.sendPasswordReset(email);
await tg.auth.signOut(); // back to a fresh anonymous identity Sign in with Google
Enable Google per game in the same panel; then tg.auth.signInWithGoogle() runs the whole OAuth flow in a popup and resolves exactly like signInWithPassword (same outcomes, same merge). Call it from a click handler (browsers block popups opened without a user gesture). It uses one Triggair-hosted callback, so you never register a redirect URI per game. providers() tells you which are enabled, so you can show the right buttons.
if ((await tg.auth.providers()).includes('google')) {
loginButton.onclick = async () => {
const r = await tg.auth.signInWithGoogle();
if (r.outcome === 'conflict') await tg.auth.resolveMerge('keep_account');
};
} - Getting started
Keys, anonymous identity, and the one-import integration loop.
- Customizing account emails
Override the HTML of the emails Triggair sends your players (confirm signup, reset password, and more), per game, from the dashboard, API, or MCP.
- Reliability: retries, idempotency and offline
How the SDK survives dropped connections and retries: the durable outbox, idempotency keys, and the error contract.
- Built for coding agents
llms.txt, an MCP server, self-fixing errors, and a one-command self-test, so an agent can integrate the backend itself.
- Game engines and raw HTTP
Integrate from Unity, Godot, or any HTTP client. The SDK is JavaScript, but the API is plain REST with header auth and a full OpenAPI spec.