> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jaza.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Started with Jaza in Minutes

> Install the Node SDK, create your first customer, load credits, and make your first consume() call. From zero to billing in under 5 minutes.

This guide takes you from zero to a working prepaid flow using **Expo / React Native** and **Node.js**.

<Tip>
  Jaza collects Mobile Money and settles to you. You do not create a PawaPay account or manage multi-currency settlement yourself.
</Tip>

## Prerequisites

* A [Jaza](https://jaza.dev) developer account
* Node.js 18+
* An Expo app (Expo Router or classic `App.tsx`)
* Sandbox API keys from the [dashboard](https://jaza.dev/dashboard) (test environment)

## 1. Create an app and copy keys

In the dashboard, create an app, enable the markets you need, and open **API keys**.

| Key         | Where it lives                                  | Example prefix |
| ----------- | ----------------------------------------------- | -------------- |
| Secret      | Server only (`JAZA_SECRET_KEY`)                 | `jz_test_sk_…` |
| Publishable | Mobile app (`EXPO_PUBLIC_JAZA_PUBLISHABLE_KEY`) | `jz_test_pk_…` |

<Warning>
  Never ship the secret key in a mobile binary or public repo.
</Warning>

## 2. Install the Node SDK

```bash theme={null}
npm install @jazadev/node
```

Create a shared client:

```ts theme={null}
import { Jaza } from '@jazadev/node';

export const jaza = new Jaza({
  secretKey: process.env.JAZA_SECRET_KEY!,
  publicKey: process.env.JAZA_PUBLIC_KEY!,
});
```

## 3. Create a customer

Call once when your user signs up; store `customer.id` on your user record.

```ts theme={null}
const customer = await jaza.createCustomer({
  name: 'Amina Okello',
  email: 'amina@example.com', // and/or phoneNumber
});

// persist customer.id → your DB
```

## 4. Expose balance and top-up routes

Your app never talks to Jaza with the secret key. It calls **your** backend; you call the Node SDK.

```ts theme={null}
import { jaza } from './jaza';

// GET /jaza/balance — resolve customerId from your session
app.get('/jaza/balance', async (req, res) => {
  const wallet = await jaza.getBalance({
    customerId: req.user.jazaCustomerId,
  });
  res.json({ balanceCredits: wallet.balanceCredits });
});

// POST /jaza/top-up-token
app.post('/jaza/top-up-token', async (req, res) => {
  const session = await jaza.topUp({
    customerId: req.user.jazaCustomerId,
  });
  res.json({ token: session.token });
});
```

## 5. Install the React Native SDK

```bash theme={null}
npm install @jazadev/react-native
```

Peer dependencies (Expo):

```bash theme={null}
npx expo install react react-native react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-screens @gorhom/bottom-sheet @expo/vector-icons
```

Ensure Android resizes with the keyboard (`app.json`):

```json theme={null}
{
  "expo": {
    "android": {
      "softwareKeyboardLayoutMode": "resize"
    }
  }
}
```

## 6. Wrap the app with `JazaProvider`

<Tabs>
  <Tab title="Expo Router">
    ```tsx theme={null}
    // app/_layout.tsx
    import { Stack } from 'expo-router';
    import { GestureHandlerRootView } from 'react-native-gesture-handler';
    import { JazaProvider } from '@jazadev/react-native';

    const API_BASE = process.env.EXPO_PUBLIC_API_URL!;

    export default function RootLayout() {
      return (
        <GestureHandlerRootView style={{ flex: 1 }}>
          <JazaProvider
            publishableKey={process.env.EXPO_PUBLIC_JAZA_PUBLISHABLE_KEY!}
            getBalance={async () => {
              const res = await fetch(`${API_BASE}/jaza/balance`, {
                credentials: 'include',
              });
              const data = await res.json();
              return data.balanceCredits as number;
            }}
            onTopUpComplete={({ credits }) => {
              console.log('Top-up completed', credits);
            }}
            theme="system"
          >
            <Stack />
          </JazaProvider>
        </GestureHandlerRootView>
      );
    }
    ```
  </Tab>

  <Tab title="Classic App">
    ```tsx theme={null}
    // App.tsx
    import { GestureHandlerRootView } from 'react-native-gesture-handler';
    import { JazaProvider } from '@jazadev/react-native';
    import { HomeScreen } from './screens/HomeScreen';

    export default function App() {
      return (
        <GestureHandlerRootView style={{ flex: 1 }}>
          <JazaProvider
            publishableKey={process.env.EXPO_PUBLIC_JAZA_PUBLISHABLE_KEY!}
            getBalance={async () => {
              const res = await fetch(
                `${process.env.EXPO_PUBLIC_API_URL}/jaza/balance`,
              );
              return (await res.json()).balanceCredits;
            }}
            theme="system"
          >
            <HomeScreen />
          </JazaProvider>
        </GestureHandlerRootView>
      );
    }
    ```
  </Tab>
</Tabs>

## 7. Show balance and top-up

```tsx theme={null}
import { View } from 'react-native';
import { JazaBalanceWidget, JazaTopUpButton } from '@jazadev/react-native';

const API_BASE = process.env.EXPO_PUBLIC_API_URL!;

export default function HomeScreen() {
  return (
    <View style={{ padding: 20, gap: 16 }}>
      <JazaBalanceWidget />
      <JazaTopUpButton
        label="Top up credits"
        onRequestToken={async () => {
          const res = await fetch(`${API_BASE}/jaza/top-up-token`, {
            method: 'POST',
            credentials: 'include',
          });
          if (!res.ok) throw new Error('Could not start top-up');
          const data = await res.json();
          return data.token as string;
        }}
      />
    </View>
  );
}
```

When the user taps **Top up**, the SDK opens a sheet: pick a bundle, enter phone / country, pay with Mobile Money. Jaza handles provider selection and collection. On success, `getBalance` refreshes.

## 8. Meter a feature

After the user performs a paid action:

```ts theme={null}
await jaza.consume({
  customerId: req.user.jazaCustomerId,
  featureCode: 'SEND_MESSAGE', // configured in the dashboard
  idempotencyKey: `msg_${messageId}`,
});
```

Use a stable `idempotencyKey` so retries do not double-charge.

## What you just shipped

* Credit wallet per customer
* MoMo top-ups without running an aggregator
* Feature metering on your backend

Next: [Concepts](/guides/concepts) · [Authentication](/guides/authentication) · [Node SDK](/sdks/node) · [React Native SDK](/sdks/react-native)
