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

# Example app

> The whole pattern, as one small readable app.

`examples/ledgerly` in the `payman-connect` repo is **Ledgerly**: a bookkeeping product with a chat box, built on the SDK. Ask it to pay an invoice and a connect button appears in the conversation; consent happens on Payman's pages, and the answer is already streaming when you land back. A payment over threshold freezes into an approval card and settles to "Payment sent".

Ledgerly's own agent is a real model with `payman.tool()` as its only tool (a labelled keyword stub fills in when no model key is set).

```bash theme={null}
# from the payman-connect repo root
npm install && npm run build

cd examples/ledgerly
cp .env.example .env      # paste your app key into PAYMAN_APP_KEY
npm start                 # http://localhost:4321
```

Register `http://localhost:4321/payman/callback` in the developer console; the app key is the whole configuration.

<Note>
  Against a local stack, point `PAYMAN_BASE_URL` at it (`.env.example` already does). Otherwise the SDK talks to production, where the app self-read fails and the client throws on the first call.
</Note>

Every Payman line is tagged `── PAYMAN`, so the whole integration is one grep. `examples/nextjs` (port 3000) and `examples/slack` are the [App Router](/nextjs) and no-browser siblings.

## Worth stealing

* `SameSite=Lax` on the session cookie (`sessions.mjs`): `Strict` drops it on the consent return, in production only.
* `refuseRequest` in `server.mjs`: Host, `Content-Type`, `Sec-Fetch-Site`, `Origin`, checked before any route dispatches.
* The prose before the connect button (`stub.mjs`): bank access always gets a reason first.
* The model-loop invariants: a round-trip cap and exactly one `tool_result` per `tool_use` in `assistant.mjs`, the rollback on a failed turn in `server.mjs`.
* What the page never holds: both keys stay server-side, and the browser talks only to this app's origin.
