URL: https://wattfare.com/docs/quickstart
# Quickstart

> From zero to a working “Connect AI budget” button in about five minutes. Five steps: install, get keys, mint sessions, connect, infer.

> **Before you start**
>
> You'll need a Wattfare app (free — create one in the [dashboard](https://wattfare.com/dashboard)) and an app of your own with some notion of a signed-in user. Wattfare keys everything by your own user ids, so there's no separate account for your end users to make.

## 1 · Install the SDK

The package ships server, client, and React entry points from one install. `ai` and `react` are optional peer dependencies — add them only if you use the inference helpers or the React binding.

```bash
npm install wattfare

# peer deps — only if you use them:
npm install ai      # inference helpers (Vercel AI SDK)
npm install react   # the React binding
```

## 2 · Get your keys

Create an app in the [developer dashboard](https://wattfare.com/dashboard). You'll get two keys with very different jobs:

| Key | Format | Where it lives | Secret? |
| --- | --- | --- | --- |
| Publishable | pk_live_{appId} | Browser — passed to the client / provider | No |
| Secret | sk_live_{appId}_{secret} | Backend only — mints sessions, runs inference | Yes |

Store them as environment variables. The secret key must never reach the browser.

```bash
# .env  (server — never ship to the browser)
WATTFARE_SECRET_KEY=sk_live_myapp_abc123...

# public — safe in the browser
PUBLIC_WATTFARE_KEY=pk_live_myapp
```

> **The secret key is shown once**
>
> Copy it the moment you create the app. If you lose it, rotate it from the dashboard — the old one stops working immediately.

## 3 · Mint session tokens on your backend

The browser can't hold your secret key, so it asks your backend for a short-lived token first. `sessionHandler()` turns that into a one-line route in any framework that speaks `Request → Response` (Next.js, Astro, Hono, Remix, Workers…).

```ts
import { Wattfare } from "wattfare/server";

const wf = new Wattfare({ secretKey: process.env.WATTFARE_SECRET_KEY! });

// sessionHandler() returns a standard Request -> Response handler.
// resolveUser returns your app's user id, or null to answer 401.
export const POST = wf.sessionHandler(
  async (req) => {
    const session = await getSession(req); // <- your existing auth
    return session?.userId ?? null;
  },
  { requestLimit: { monthlyUsd: 10 } },    // optional default cap to suggest
);
```

`resolveUser` receives the raw request — plug in your existing auth and return the user's id, or `null` to answer `401`. The optional second argument suggests a budget cap the user will see in the consent popup.

## 4 · Add the connect button

On the frontend, point the SDK at the route you just made. In React, wrap your app once and read everything from the `useWattfare()` hook:

```tsx
import { WattfareProvider, useWattfare } from "wattfare/react";

function App() {
  return (
    <WattfareProvider
      publishableKey={import.meta.env.PUBLIC_WATTFARE_KEY}
      session="/api/wattfare-token"
    >
      <Chat />
    </WattfareProvider>
  );
}

function Chat() {
  const { connected, connect, remainingUsd, loading } = useWattfare();

  if (loading) return <p>Loading…</p>;
  if (!connected) return <button onClick={connect}>⚡ Connect AI budget</button>;

  return (
    <div>
      <p>Budget left: {remainingUsd === null ? "unlimited" : `$${remainingUsd}`}</p>
      {/* your chat UI */}
    </div>
  );
}
```

Not using React? The vanilla client is the same idea without the context:

```ts
import { createWattfare } from "wattfare/client";

const wf = createWattfare({
  publishableKey: "pk_live_…",
  session: "/api/wattfare-token",
});

// IMPORTANT: call connect() straight from the click handler, or popup
// blockers will kill it — the popup must open inside the user gesture.
connectBtn.onclick = () => wf.connect();
```

> **Call connect() from a click**
>
> Opening the consent popup must happen inside a user gesture. The SDK opens a blank popup synchronously before any `await`, but if you wrap `connect()` in your own async work first, the browser will block it.

## 5 · Make an inference call

Back on your server, `ai.model(...)` returns a standard AI SDK model. Pass it to `streamText` / `generateText` like any other provider — usage is metered against the user's budget automatically.

```ts
import { Wattfare, isBudgetExceeded } from "wattfare/server";
import { streamText } from "ai";

const wf = new Wattfare({ secretKey: process.env.WATTFARE_SECRET_KEY! });

export async function POST(req: Request) {
  const { prompt, userId } = await req.json();
  const ai = wf.user(userId);

  // "Not connected" is the expected first-run state — handle it as a branch.
  const status = await ai.status();
  if (!status.connected) {
    return Response.json({ error: "not_connected" }, { status: 402 });
  }

  try {
    const result = streamText({
      model: ai.model("openai/gpt-4o-mini"), // any OpenRouter model id
      prompt,
    });
    return result.toTextStreamResponse();
  } catch (err) {
    if (isBudgetExceeded(err)) {
      return Response.json({ error: "budget_exceeded" }, { status: 402 });
    }
    throw err;
  }
}
```

## That's it

You now have the full loop: a user connects their budget once, and every model call you make on their behalf is metered and capped. From here:

- [How it works](https://wattfare.com/docs/concepts) — Understand the consent flow, sessions, and metering so the pieces above click into place.
- [Error handling](https://wattfare.com/docs/errors) — Turn “not connected”, “budget exceeded”, and rate limits into graceful UI.
- [Next.js guide](https://wattfare.com/docs/guides/nextjs) — The same flow as a complete App Router project you can copy wholesale.
- [Budget UI patterns](https://wattfare.com/docs/guides/budget-ui) — Connect buttons, budget bars, and depletion warnings that earn user trust.
