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

# Your backend

> Stand up the wallet routes with the Meru Node SDK — one handler, your secret api-key stays server-side.

The SDKs' `backendBase` points at a thin server you run. It does two things: it
holds your **secret** Meru api-key (never the frontend), and it serves the wallet
actions the app calls. The Meru Node SDK gives you both in a single handler.

## Install

```bash theme={null}
npm i @meru.app/node
```

Set your secret api-key as a server-side environment variable:

```bash theme={null}
MERU_API_KEY=sk_live_...
```

<Warning>
  `MERU_API_KEY` is a **secret**. Keep it on the server — never ship it to the app
  or expose it in client code. The frontend only ever holds the short-lived session
  token from sign-in.
</Warning>

## Mount the handler

`createMeruHandler` returns a standard Web `Request → Response` function, so it drops
into Next.js, Hono, Bun, and Deno. Mount it at the path you use for `backendBase`
(here `/api/meru`).

```ts app/api/meru/[...meru]/route.ts (Next.js) theme={null}
import { MeruClient, createMeruHandler } from "@meru.app/node";

const handler = createMeruHandler({
  client: new MeruClient({ apiKey: process.env.MERU_API_KEY! }),
  basePath: "/api/meru",

  // Self-custody: never auto-create a managed wallet. The user opens their own
  // on-device wallet deliberately (that's what the app's `enroll()` does).
  accountMode: "self-custody",

  // The app sends the user's Meru session token as the Bearer. Identify the user
  // from it — the wallet routes verify the token for real when signing.
  resolveUser: (req) => {
    const id = userIdFromMeruToken(req.headers.get("authorization"));
    return id ? { id } : null;
  },
});

export { handler as GET, handler as POST };
```

`userIdFromMeruToken` just reads the user id out of the session token (a JWT whose
`sub` is `customer:<companyId>:<subjectId>` — the wallet is keyed on `subjectId`):

```ts theme={null}
function userIdFromMeruToken(authorization: string | null): string | null {
  const token = (authorization ?? "").replace(/^Bearer\s+/i, "");
  if (!token) return null;
  try {
    const payload = JSON.parse(
      Buffer.from(token.split(".")[1], "base64url").toString(),
    );
    const sub: string = payload.sub ?? "";
    return sub.split(":").slice(2).join(":") || null; // the subjectId
  } catch {
    return null;
  }
}
```

## Other frameworks

Because the handler is `(Request) => Promise<Response>`:

<CodeGroup>
  ```ts Hono theme={null}
  app.all("/api/meru/*", (c) => handler(c.req.raw));
  ```

  ```ts Bun theme={null}
  Bun.serve({ port: 3200, fetch: handler });
  ```

  ```ts Deno theme={null}
  Deno.serve((req) => handler(req));
  ```
</CodeGroup>

For Express (Node's `req`/`res`), wrap it with a small Request/Response adapter, or
run it behind an edge/runtime that speaks the Web `Request` type.

## What it serves

Mounted at `/api/meru`, the handler answers exactly the routes the wallet client
calls — you don't implement these yourself:

| Route                                                    | Used by             |
| -------------------------------------------------------- | ------------------- |
| `GET /accounts/me`                                       | `wallet.status()`   |
| `POST /accounts/self-custody`                            | `wallet.enroll()`   |
| `POST /accounts/self-custody/activate/build` · `/submit` | `wallet.activate()` |
| `POST /accounts/self-custody/recover`                    | `wallet.recover()`  |
| `POST /pay/crypto/build` · `/submit`                     | `wallet.send()`     |

<Note>
  This is the whole backend for a self-custody wallet: one handler, one secret key.
  Balances and history that the public API can't derive for you (if you want them)
  are added as optional resolvers on `createMeruHandler` — see the Node SDK reference.
</Note>
