Orishare
Recipes

React Native and Expo

One prompt: XP, levels, coins, streaks, badges, quests, leaderboards, leagues, referrals and push in a React Native or Expo app.

A React Native or Expo app reads Orishare as the signed-in user: the publishable key plus a customer session. The default is server first: your backend holds the secret key, mints the session and reports what happened. With Firebase that backend is the Cloud Functions kit (Firebase app), with Supabase the Edge Function template (Supabase app), otherwise your own server (Web game, step 5). Never put a secret key (osk_) or the agent token in the app or in EXPO_PUBLIC_* variables: they ship in the bundle.

The prompt

Paste this into your coding agent from the root of the app.

Add a reward system to this React Native / Expo app with Orishare: XP and levels, coins, a daily
streak with repair, badges, a starter quest, a weekly XP leaderboard, a weekly league, friend
referrals, friends with friend streaks and a weekly friend quest, and push notifications. Follow https://docs.orishare.com/docs/recipes/react-native.md
and the pages it links, step by step: sign up for Orishare yourself (no human needed), apply the
game starter configuration in the test environment, add the client module and screens the recipe
describes, test every HTTP call the app makes against the test environment, then give me the claim link
and a short list of what you built. Never put a secret key (osk_) or agent token (opat_) in the app.

Steps

  1. Sign up with Start here for AI agents, then apply the game starter in the test environment: Game setup. For an app that is not a game, rename the events (lesson.completed for level.completed) in the change set before you apply it; the mechanics are the same.

  2. Get a session for the user from your backend. Firebase: deploy the @orishare/firebase-functions kit (steps 2 to 5 of Firebase app) and call orishareSession with @react-native-firebase/functions (or the firebase/functions JS SDK). Supabase: deploy the Orishare Edge Function (steps 2 to 4 of Supabase app) and call it with supabase.functions.invoke('orishare', { body: { action: 'session' } }). Any other backend mints it with the secret key (Web game, step 5). Each answers customer_token and expires_at; pass the token to useServerSession below and fetch a new one shortly before it expires. Exchanging the Firebase ID token or Supabase access token in the app instead (Game setup, identity) works without a backend, with client trust and caps; use one path per app, never both.

  3. The client module (lib/orishare.ts):

    import * as Crypto from 'expo-crypto'; // or crypto.randomUUID() where available
    
    const API = 'https://api.orishare.com';
    const PUBLISHABLE_KEY = process.env.EXPO_PUBLIC_ORISHARE_PUBLISHABLE_KEY!; // opk_, public
    const CONNECTION_ID = process.env.EXPO_PUBLIC_ORISHARE_CONNECTION_ID; // ic_, public
    let token: string | null = null; // memory only: not AsyncStorage or SecureStore
    
    // Without a backend only (client trust): exchange the provider token in the app.
    export async function exchange(providerToken: string) {
      const res = await fetch(`${API}/v1/customer_sessions`, {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${PUBLISHABLE_KEY}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': Crypto.randomUUID(),
        },
        body: JSON.stringify({
          grant_type: 'identity_token',
          connection_id: CONNECTION_ID,
          provider_token: providerToken,
        }),
      });
      token = res.ok ? (await res.json()).customer_token : null;
      return token !== null;
    }
    
    export function useServerSession(serverToken: string) {
      token = serverToken;
    }
    
    export async function customer<T>(
      path: string,
      init: { method?: string; body?: unknown; key?: string } = {},
    ): Promise<T> {
      const method = init.method ?? 'GET';
      const res = await fetch(API + '/v1/customer/' + path, {
        method,
        headers: {
          Authorization: `Bearer ${PUBLISHABLE_KEY}`,
          'X-Orishare-Customer-Token': token ?? '',
          'Content-Type': 'application/json',
          ...(method !== 'GET' ? { 'Idempotency-Key': init.key ?? Crypto.randomUUID() } : {}),
        },
        body: init.body === undefined ? undefined : JSON.stringify(init.body),
      });
      if (res.status === 401) token = null; // expired: exchange again
      if (!res.ok) throw new Error(`orishare ${res.status}`);
      return res.json() as Promise<T>;
    }
    
    export const track = (
      name: string,
      properties: Record<string, unknown> = {},
      id = Crypto.randomUUID(),
    ) => customer('events', { method: 'POST', body: { id, name, properties } });

    Retry an intent with the same key and event id, never a new one.

  4. What the app sends. Through your backend (server trust, no caps): session.started once a day on open, level.completed and match.completed. With the Firebase kit that is the orishareEvents callable, with the Supabase function the events action, each event with an operation_id you reuse on retry (session-<UTC day>, level-<n>); your own server sends them with the secret key (Web game, step 4). Only an app with no backend sends them itself with track, counted up to the per-day caps.

  5. Screens: Game setup, player screens. Players appear on the weekly board only after they opt in (POST /v1/customer/leaderboard_profiles). Refresh them when the app returns to the foreground (AppState), not on a timer.

  6. Push. Get the native device token with expo-notifications ((await Notifications.getDevicePushTokenAsync()).data) and register it: customer('notification_devices', { method: 'POST', body: { kind: Platform.OS === 'ios' ? 'apns' : 'fcm', token, platform: Platform.OS } }). Which messages are sent: Game setup, notifications. Orishare sends no push until a person has claimed the project.

  7. Friends. Apply /recipes/social.json and add the friends screens from Game setup, friends and social: share the friend code (a deep link or QR), requests, the friend list with each friend streak's flame and at_risk, a nudge button, the weekly friend quest and league friend standings. Friend streak days and friend quest progress are recorded by your backend with the secret key when a lesson or level is finished: orishareFriendProgress (Firebase kit) or the friend_progress action (Supabase function).

  8. Test the exact calls from a terminal against the test environment (Game setup, test), then in the simulator. Native requests send no Origin and need no allowed origin; Expo web does (Web game, step 7).

  9. Go live: Game setup, go live.

On this page