URL: https://wattfare.com/docs/server-sdk
# Server SDK

> Import from wattfare/server. This runs on your backend, holds the secret key, and brokers everything that must never touch the browser — minting sessions and running inference.

## new Wattfare(config)

The server entry point. Construct it once with your secret key and reuse it.

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

const wf = new Wattfare({
  secretKey: process.env.WATTFARE_SECRET_KEY!, // required, starts with "sk_"
  baseURL: "https://wattfare.com",             // optional override
  fetch: customFetch,                          // optional, for tests/runtimes
});
```

| Option | Type | Default | Notes |
| --- | --- | --- | --- |
| secretKey | string | — | Required. Must start with `sk_` or the constructor throws `WattfareAuthError`. |
| baseURL | string | https://wattfare.com | Point at a different Wattfare service (self-host / staging). |
| fetch | typeof fetch | global fetch | Inject a custom fetch for tests or non-standard runtimes. |

## Instance methods

| Method | Returns | Description |
| --- | --- | --- |
| user(appUserId) | WattfareUser | Scope all operations to one of your user ids. |
| createSession(appUserId, request?) | Promise<CreatedSession> | Mint a short-lived JWT for the frontend. |
| sessionHandler(resolveUser, request?) | (req) => Promise<Response> | A drop-in route handler that mints sessions. |
| grant(grantToken) | WattfareGrant | Redeem a one-time access grant. Returns a handle whose `model()` spends against the grant. |

## wf.user(appUserId)

Returns a `WattfareUser` bound to one of your users. Pass **your own** user id — Wattfare keys all state by `appId:appUserId`, so there's nothing to map.

```ts
const ai = wf.user("user_123"); // your own user id

// 1) Inference — a standard AI SDK model, billed to this user.
import { generateText } from "ai";
const { text } = await generateText({
  model: ai.model("anthropic/claude-sonnet-4"),
  prompt: "Summarise this in one line.",
});

// 2) Status — connection + budget snapshot (never throws on first run).
const status = await ai.status();
if (!status.connected) {
  // prompt the user to connect their budget
}
```

| Method | Returns | Description |
| --- | --- | --- |
| model(modelId) | LanguageModelV3 | An AI-SDK-compatible model. Requests proxy through Wattfare with the user header set. Accepts any OpenRouter model id. |
| status() | Promise<ConnectionStatus> | The user's connection + budget snapshot. Returns `{ connected: false }` rather than throwing when they haven't connected. |

> **model() is a real AI SDK provider**
>
> The return value works with `generateText`, `streamText`, `generateObject`, tools — anything in the Vercel AI SDK. There's nothing custom to learn; Wattfare just sits in the transport.

## wf.grant(grantToken)

Redeems a one-time access grant token from the browser. Returns a `WattfareGrant` handle whose `model()` works like `WattfareUser.model()` — no user id needed. See [One-time grants](https://wattfare.com/docs/grants) for the full guide.

## wf.sessionHandler(resolveUser, request?)

Builds a `Request → Response` handler that mints session tokens. This is the recommended way to expose your token route — it handles auth failures and error shaping for you.

```ts
// Next.js — app/api/wattfare-token/route.ts
export const POST = wf.sessionHandler(
  async (req) => getUserId(req),         // -> your user id, or null -> 401
  { requestLimit: { monthlyUsd: 20 } },  // optional cap the user approves
);

// The second arg can also be a function of the request, e.g. per-plan caps:
export const POST = wf.sessionHandler(
  (req) => getUserId(req),
  async (req) => ({ requestLimit: { monthlyUsd: await capFor(req) } }),
);
```

| Parameter | Type | Description |
| --- | --- | --- |
| resolveUser | (req) => string \| null \| Promise<…> | Resolve the signed-in user from the request. Return your user id, or `null` to answer `401`. |
| request | SessionRequest \| (req) => SessionRequest | Optional. The cap to request, or a function of the request for dynamic caps. |

On success it responds `{ token, expiresAt }`. On a thrown `WattfareError` it responds with that error's JSON and status; on anything else, a generic `500`.

## wf.createSession(appUserId, request?)

The lower-level primitive behind `sessionHandler`. Reach for it when you need to shape the response yourself or aren't in a `Request → Response` runtime.

```ts
const { token, expiresAt } = await wf.createSession("user_123", {
  requestLimit: { monthlyUsd: 20 }, // optional
});

// Hand 'token' to the frontend however you like (it's just a string).
return Response.json({ token, expiresAt });
```

| Field | Type | Description |
| --- | --- | --- |
| token | string | The short-lived JWT. Pass it to the frontend client. |
| expiresAt | number | Unix epoch (seconds). Default TTL is 10 minutes. |

## Types

Re-exported from `wattfare/server` for convenience.

```ts
interface ConnectionStatus {
  connected: boolean;
  limits: { monthlyUsd: number | null } | null;
  usage: { monthlyUsd: number };
  remainingUsd: number | null; // null = unlimited
}
```

| Type | Shape |
| --- | --- |
| Limits | `{ monthlyUsd: number \| null }` |
| Usage | `{ monthlyUsd: number }` |
| SessionRequest | `{ requestLimit?: { monthlyUsd?: number } }` |
| CreatedSession | `{ token: string; expiresAt: number }` |
| GrantResult | `{ grantToken: string; expiresAt: number; requests: number }` |

> **Note**
>
> Error classes and the `isBudgetExceeded` / `isNotConnected` / `toWattfareError` helpers are also exported here. See [Error handling](https://wattfare.com/docs/errors).
