Guest to registered — try before register
The “try before register” UX is one of the most effective ways to reduce
sign-up friction. Rakomi supports it natively via anonymous sign-ins and
a lossless claim operation: the users.id is preserved across the
transition, so any data your app attached to the guest (cart, metadata,
preferences) follows the user into their registered account without any
migration code.
Positioning. Unlike enterprise-only providers (Stytch, WorkOS), Rakomi supports guest sessions for consumer and B2C workloads. Firebase’s
linkWithCredential()and Supabase’s anonymous sign-ins cover the same shape — Rakomi’s claim flow reads as the OAuth-ecosystem-neutral equivalent.
The flow
Section titled “The flow”┌──────────────────┐ POST /v1/auth/anonymous ┌──────────────────────┐│ Visitor lands │──────────────────────────▶│ Guest session ││ (no account) │ │ is_anonymous: true │└──────────────────┘ └──────────┬───────────┘ │ user fills cart │ submits order ▼ POST /v1/auth/register + anon bearer │ ▼ ┌──────────────────────┐ │ Registered user │ │ same users.id │ │ is_anonymous: false │ │ claimed_at: now() │ └──────────────────────┘E-commerce cart example
Section titled “E-commerce cart example”Anna lands on your storefront without an account. She adds items to her cart, reaches checkout, and only then registers. You want the cart to survive the registration.
1. Mint a guest session when the user interacts
Section titled “1. Mint a guest session when the user interacts”import { useAnonymousSignin, useAuth } from '@rakomi/react';
export function AddToCartButton({ productId }: { productId: string }) { const auth = useAuth(); const { signIn } = useAnonymousSignin();
async function onAdd() { if (!auth.isLoaded) return; if (!auth.isSignedIn) { // Create the guest on the first "Add to cart" click — NOT on page load // (Minimise MAU churn: zero-intent visits do not consume MAU). await signIn({ publicMetadata: { entry_point: 'product_page' } }); } await fetch('/api/cart', { method: 'POST', body: JSON.stringify({ productId }) }); }
return <button onClick={onAdd}>Add to cart</button>;}2. Attach cart state to the user id
Section titled “2. Attach cart state to the user id”On the server side, key the cart by users.id (e.g. user_id column or
document key). Because the id does not change at claim time, you do not need
to migrate anything.
3. Registration preserves the id
Section titled “3. Registration preserves the id”import { useAuth } from '@rakomi/react';
export function CheckoutRegisterForm() { const { getToken } = useAuth(); async function onSubmit(email: string, password: string) { const token = await getToken(); if (!token.ok) return; await fetch('/v1/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token.token}`, 'X-API-Key': process.env.NEXT_PUBLIC_RAKOMI_API_KEY!, }, body: JSON.stringify({ email, password, consent: true }), }); // The underlying users.id is unchanged — the cart survives. } // ...}After registration, auth.user.isAnonymous flips to false on the next token
refresh. The session remains valid, the cart remains valid — no “welcome
back, please re-add” dialog necessary.
What happens to unclaimed guests?
Section titled “What happens to unclaimed guests?”Unclaimed guests are hard-deleted by a daily cron at 02:30 UTC once
last_active_at exceeds the tenant’s configured retention (1–90 days,
default 30). See Settings → Authentication → Anonymous in the dashboard.
Upgrade via social OAuth
Section titled “Upgrade via social OAuth”In addition to email + password, a guest can claim their session by signing in
with a social provider (Google, GitHub, Microsoft, Apple, Discord, Facebook,
Slack, Twitter/X, GitLab, LinkedIn). The flow is identical from the developer’s
perspective: send the anonymous bearer on the /oauth/{provider}/authorize
initiate call, then let the user complete the provider’s consent screen. The
callback will update the same users.id in place — no new user row is created,
and every FK-keyed downstream row (sessions, metadata, org memberships) carries
over exactly as in the email-claim flow. never_trust providers (Facebook,
Twitter) are the exception: per industry-standard anti-takeover practice, they
do NOT promote the anon row by email and instead create a fresh account.
What does NOT carry over
Section titled “What does NOT carry over”- Tokens and sessions — the anonymous bearer is revoked at claim time; the user is expected to re-authenticate with the regular post-registration flow.
- Data that is explicitly keyed on “is anonymous” state (rare — most apps
key by
user_id).
Billing
Section titled “Billing”Anonymous users count toward your MAU quota from the moment they are created. Hard deletion mid-month does not refund MAU credit — this mirrors the Rakomi MAU-peak billing model (see the pricing page) and is consistent with industry practice.
Further reading
Section titled “Further reading”- API reference: Anonymous sign-ins
- Security posture: short-lived pseudonymous identifier, no PII on the user row, GDPR Art. 4(5) pseudonymisation.