bkper 4.13.7 → 4.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/lib/commands/apps/build.d.ts +1 -2
  2. package/lib/commands/apps/build.d.ts.map +1 -1
  3. package/lib/commands/apps/build.js +13 -39
  4. package/lib/commands/apps/build.js.map +1 -1
  5. package/lib/commands/apps/config.d.ts +5 -14
  6. package/lib/commands/apps/config.d.ts.map +1 -1
  7. package/lib/commands/apps/config.js +13 -45
  8. package/lib/commands/apps/config.js.map +1 -1
  9. package/lib/commands/apps/deploy.d.ts +14 -4
  10. package/lib/commands/apps/deploy.d.ts.map +1 -1
  11. package/lib/commands/apps/deploy.js +42 -58
  12. package/lib/commands/apps/deploy.js.map +1 -1
  13. package/lib/commands/apps/dev.d.ts +1 -7
  14. package/lib/commands/apps/dev.d.ts.map +1 -1
  15. package/lib/commands/apps/dev.js +43 -115
  16. package/lib/commands/apps/dev.js.map +1 -1
  17. package/lib/commands/apps/init.d.ts +1 -1
  18. package/lib/commands/apps/init.d.ts.map +1 -1
  19. package/lib/commands/apps/init.js +6 -12
  20. package/lib/commands/apps/init.js.map +1 -1
  21. package/lib/commands/apps/register.d.ts.map +1 -1
  22. package/lib/commands/apps/register.js +3 -11
  23. package/lib/commands/apps/register.js.map +1 -1
  24. package/lib/commands/apps/types.d.ts +3 -27
  25. package/lib/commands/apps/types.d.ts.map +1 -1
  26. package/lib/dev/local-assets.d.ts +15 -0
  27. package/lib/dev/local-assets.d.ts.map +1 -0
  28. package/lib/dev/local-assets.js +43 -0
  29. package/lib/dev/local-assets.js.map +1 -0
  30. package/lib/dev/logger.d.ts +3 -7
  31. package/lib/dev/logger.d.ts.map +1 -1
  32. package/lib/dev/logger.js +3 -6
  33. package/lib/dev/logger.js.map +1 -1
  34. package/lib/dev/miniflare.d.ts +2 -0
  35. package/lib/dev/miniflare.d.ts.map +1 -1
  36. package/lib/dev/miniflare.js +83 -21
  37. package/lib/dev/miniflare.js.map +1 -1
  38. package/lib/docs/app-building.md +151 -141
  39. package/lib/docs/app-management.md +43 -28
  40. package/lib/docs/bkper-js.md +2 -0
  41. package/lib/docs/index.md +1 -1
  42. package/lib/platform/client.d.ts +2 -2
  43. package/lib/platform/client.js +2 -2
  44. package/package.json +1 -1
@@ -86,32 +86,38 @@ source: /docs/build/apps/architecture.md
86
86
 
87
87
  # App Architecture
88
88
 
89
- Bkper platform apps follow a three-package monorepo pattern. Each package handles a distinct concern, all deployed to the same `{appId}.bkper.app` domain.
89
+ Bkper platform apps use one Worker bundle per app and environment. The same Worker serves the browser client, app-defined `/api/*` routes, and Bkper event ingress at `/events`.
90
90
 
91
91
  ## Structure
92
92
 
93
- ```
94
- packages/
95
- ├── shared/ — Shared types and utilities
96
- ├── web/
97
- ├── client/ — Frontend UI (Vite + Lit)
98
- │ └── server/ — Backend API (Hono)
99
- └── events/ — Event handler (webhooks)
93
+ ```txt
94
+ my-app/
95
+ ├── client/
96
+ ├── index.html
97
+ └── src/ — Frontend UI (Vite + Lit)
98
+ ├── server/
99
+ └── src/
100
+ │ ├── index.ts
101
+ │ └── handlers/
102
+ ├── bkper.yaml
103
+ ├── package.json
104
+ ├── tsconfig.json
105
+ └── vite.config.ts
100
106
  ```
101
107
 
102
- The packages are connected via [Bun workspaces](https://bun.sh/docs/install/workspaces). Import shared code from `@my-app/shared` in any package.
108
+ The default template is intentionally not a monorepo. Add a shared package only when the app actually needs one.
103
109
 
104
- ## Web client
110
+ ## Client
105
111
 
106
- The client package builds a browser UI with [Lit](https://lit.dev/) and [@bkper/web-design](https://www.npmjs.com/package/@bkper/web-design) for consistent Bkper styling.
112
+ The client folder builds a browser UI with [Lit](https://lit.dev/) and [@bkper/web-design](https://www.npmjs.com/package/@bkper/web-design) for consistent Bkper styling.
107
113
 
108
114
  - Built with [Vite](https://vitejs.dev/) — configured in the project's `vite.config.ts` for fast builds and HMR during development
109
- - Static assets served by the web server handler
115
+ - Built assets are deployed with the same Worker
110
116
  - Communicates with Bkper via `bkper-js`
111
117
 
112
118
  This is where your app's UI lives — book pickers, account lists, reports, forms.
113
119
 
114
- ### Web client authentication
120
+ ### Client authentication
115
121
 
116
122
  The client authenticates users via the [`@bkper/web-auth`](https://www.npmjs.com/package/@bkper/web-auth) SDK. OAuth is pre-configured on the platform — no client IDs, redirect URIs, or consent screens to set up.
117
123
 
@@ -131,53 +137,105 @@ const bkper = new Bkper({
131
137
  });
132
138
  ```
133
139
 
134
- This is the canonical pattern. Do not implement custom OAuth flows, redirect handling, or token refresh — the SDK and platform handle everything. See the [@bkper/web-auth API Reference](https://bkper.com/docs/api/bkper-web-auth.md) for the full SDK documentation.
140
+ This is the canonical browser pattern. Do not implement custom OAuth flows, redirect handling, or token refresh — the SDK and platform handle everything. See the [@bkper/web-auth API Reference](https://bkper.com/docs/api/bkper-web-auth.md) for the full SDK documentation.
135
141
 
136
- ## Web server
142
+ ## Server Worker
137
143
 
138
- The server package runs on [Cloudflare Workers](https://developers.cloudflare.com/workers/) using [Hono](https://hono.dev/) as the web framework. It handles:
144
+ The server folder runs on [Cloudflare Workers](https://developers.cloudflare.com/workers/) using [Hono](https://hono.dev/) as the web framework. It handles:
139
145
 
140
146
  - Serving the client's static assets
141
- - Custom API routes for your app's backend logic
147
+ - Custom API routes for your app's backend logic under `/api/*`
148
+ - Bkper event ingress under `/events`
142
149
  - Type-safe access to platform services (KV, secrets) via `c.env`
143
150
 
144
151
  ```ts
145
152
  import { Hono } from 'hono';
146
- import type { Env } from '../../../../env.js';
153
+ import { Bkper, Book } from 'bkper-js';
154
+ import type { Env } from '../../env.js';
147
155
 
148
156
  const app = new Hono<{ Bindings: Env }>();
149
157
 
150
- app.get('/api/data', async c => {
151
- const cached = await c.env.KV.get('my-key');
152
- return c.json({ data: cached });
158
+ app.get('/api/books', async c => {
159
+ const bkper = new Bkper();
160
+ const books = await bkper.getBooks();
161
+ return c.json({ books });
153
162
  });
154
163
 
164
+ app.post('/events', async c => {
165
+ const event: bkper.Event = await c.req.json();
166
+ const bkper = new Bkper();
167
+ const book = new Book(event.book, bkper.getConfig());
168
+ // route by event.type
169
+ return c.json({ result: false });
170
+ });
171
+
172
+ app.get('*', c => c.env.ASSETS.fetch(c.req.raw));
173
+
155
174
  export default app;
156
175
  ```
157
176
 
158
- ## Events handler
177
+ ### Server API authentication
159
178
 
160
- The events package receives webhook calls from Bkper when subscribed [events](https://bkper.com/docs/build/apps/event-handlers.md) occur. It's a separate Hono app that processes events and returns responses.
179
+ For deployed apps, server API routes under `/api/*` require a standard bearer token on the incoming request:
161
180
 
162
181
  ```ts
163
- import { Hono } from 'hono';
164
- import { Bkper, Book } from 'bkper-js';
165
- import { handleTransactionChecked } from './handlers/transaction-checked.js';
166
- import type { Env } from '../../../env.js';
182
+ const token = auth.getAccessToken();
183
+ if (!token) throw new Error('Not authenticated');
184
+
185
+ const response = await fetch('/api/data', {
186
+ headers: { Authorization: `Bearer ${token}` },
187
+ });
188
+ ```
189
+
190
+ Dispatch validates the bearer token before your Worker runs. It then strips the `Authorization` header and passes only an internal outbound context, so app code should not read user tokens from request headers.
191
+
192
+ When the server route calls Bkper, use `bkper-js` without a token provider:
193
+
194
+ ```ts
195
+ import { Bkper } from 'bkper-js';
196
+
197
+ app.get('/api/books', async c => {
198
+ const bkper = new Bkper();
199
+ const books = await bkper.getBooks();
200
+ return c.json({
201
+ books: books.map(book => ({
202
+ id: book.getId(),
203
+ name: book.getName(),
204
+ })),
205
+ });
206
+ });
207
+ ```
208
+
209
+ Platform outbound auth injects the validated user's OAuth token on exact Bkper API requests. Browser sessions only allow access to app web pages; they do not authorize `/api/*` server routes or create outbound auth context.
210
+
211
+ ### Event handler authentication
212
+
213
+ On the Bkper Platform, `/events` is an internal Bkper delivery channel on the same Worker. App code must not read `bkper-oauth-token`, `bkper-agent-id`, or `Authorization` headers.
214
+
215
+ Use the same server-side Bkper API pattern as `/api/*` routes:
216
+
217
+ ```ts
218
+ const bkper = new Bkper();
219
+ const book = new Book(event.book, bkper.getConfig());
220
+ ```
221
+
222
+ Dispatch consumes the Core-sent event access token and strips platform headers before invoking your Worker. Platform outbound auth injects the token and app agent identity when your event handler calls the Bkper API.
223
+
224
+ For [self-hosted](https://bkper.com/docs/build/apps/self-hosted.md) event handlers, you receive and process event auth headers directly because the platform outbound layer is not involved.
225
+
226
+ ## Event routing pattern
167
227
 
168
- const app = new Hono<{ Bindings: Env }>().basePath('/events');
228
+ A typical server routes events by type and delegates to small handlers:
169
229
 
170
- app.post('/', async c => {
230
+ ```ts
231
+ app.post('/events', async c => {
171
232
  const event: bkper.Event = await c.req.json();
172
233
 
173
234
  if (!event.book) {
174
235
  return c.json({ error: 'Missing book in event payload' }, 400);
175
236
  }
176
237
 
177
- const bkper = new Bkper({
178
- oauthTokenProvider: async () => c.req.header('bkper-oauth-token'),
179
- agentIdProvider: async () => c.req.header('bkper-agent-id'),
180
- });
238
+ const bkper = new Bkper();
181
239
  const book = new Book(event.book, bkper.getConfig());
182
240
 
183
241
  switch (event.type) {
@@ -187,47 +245,19 @@ app.post('/', async c => {
187
245
  return c.json({ result: false });
188
246
  }
189
247
  });
190
-
191
- export default app;
192
248
  ```
193
249
 
194
- Event handlers run at `https://{appId}.bkper.app/events` in production. During development, a Cloudflare tunnel routes events to your local machine.
250
+ Event handlers run at `https://{appId}.bkper.app/events` in production. During development, a Cloudflare tunnel routes events to the same local Worker.
195
251
 
196
252
  See [Event Handlers](https://bkper.com/docs/build/apps/event-handlers.md) for patterns and details.
197
253
 
198
- ## Shared package
199
-
200
- Common types, utilities, and constants used across packages:
201
-
202
- ```ts
203
- // packages/shared/src/types.ts
204
- export interface EventResult {
205
- result?: string | string[] | boolean;
206
- error?: string;
207
- warning?: string;
208
- }
209
-
210
- // packages/shared/src/constants.ts
211
- export const APP_NAME = 'my-app';
212
- ```
213
-
214
- Import in any package:
215
-
216
- ```ts
217
- import type { EventResult } from '@my-app/shared';
218
- ```
219
-
220
- > **Note:** The `Env` type (KV bindings, secrets) lives in the root `env.d.ts` file, auto-generated from `bkper.yaml`. Import it as `import type { Env } from '../../../env.js'` — it is not part of the shared package.
221
-
222
- ## When you don't need all three
254
+ ## When you don't need every part
223
255
 
224
256
  Not every app needs a UI, API, and event handler:
225
257
 
226
- - **Event-only app** — Just the `events` package. Automates reactions to book events without a user interface. Remove the `web` section from `bkper.yaml`.
227
- - **UI-only app** — Just the `web` packages. Opens via a [context menu](https://bkper.com/docs/build/apps/context-menu.md) to display data or collect input. Remove the `events` section from `bkper.yaml`.
228
- - **Full app** — All three packages. Interactive UI with backend logic and event-driven automation.
229
-
230
- The template includes all three by default. Remove what you don't need.
258
+ - **Event-only app** — Keep `server/` and omit `deployment.client`. Automates reactions to book events without a user interface.
259
+ - **UI-only app** — Use `client/` and keep a minimal server Worker for static assets. Remove the `events` list and `webhookUrl` if you do not handle events.
260
+ - **Full app** — Client UI, `/api/*` backend logic, and `/events` automation in one Worker.
231
261
 
232
262
  ## Simple App Patterns
233
263
 
@@ -235,10 +265,10 @@ These are the minimal, canonical patterns for common app tasks. Use them as star
235
265
 
236
266
  ### Client-only UI with authentication
237
267
 
238
- The smallest useful app needs only the `packages/web/client/` directory. No server routes, no event handlers, no custom auth logic.
268
+ The smallest useful app can keep all business logic in `client/`. No custom server routes, no event handlers, no custom auth logic.
239
269
 
240
270
  ```ts
241
- // packages/web/client/src/app.ts
271
+ // client/src/app.ts
242
272
  import { Bkper } from 'bkper-js';
243
273
  import { BkperAuth } from '@bkper/web-auth';
244
274
 
@@ -260,6 +290,7 @@ async function render() {
260
290
  ```
261
291
 
262
292
  Key points:
293
+
263
294
  - `BkperAuth` handles OAuth, token refresh, and session management internally.
264
295
  - `auth.getAccessToken()` returns a valid token synchronously after `init()` resolves.
265
296
  - Do not add server-side `/auth/*` routes. Do not implement `refresh_token` logic yourself.
@@ -280,9 +311,10 @@ Use `bkper-js` for all API calls. Do not call the REST API directly when `bkper-
280
311
  |------|-----|------------|
281
312
  | Client authentication | `@bkper/web-auth` (`BkperAuth`, `getAccessToken`) | Custom OAuth flows, manual `fetch('/auth/refresh')`, `google-auth-library` in the browser |
282
313
  | API calls from client | `bkper-js` (`Bkper`, `Book`, `Account`, `Transaction`) | Direct `fetch()` to REST endpoints |
283
- | API calls from event handler | `bkper-js` with `oauthTokenProvider` from `bkper-oauth-token` header | Hard-coding API keys, calling REST directly |
314
+ | API calls from app server `/api/*` route | Incoming `Authorization: Bearer <token>` + server-side `new Bkper()` | Reading OAuth tokens in server code, relying on browser sessions for API auth |
315
+ | API calls from platform event handler | Server-side `new Bkper()` in `/events` | Reading `bkper-oauth-token` or `bkper-agent-id` in platform app code |
284
316
  | Local development server | `npm run dev` (template script) | Manual `miniflare` + `cloudflared` invocations |
285
- | Event handler routing | `switch (event.type)` in `packages/events/src/index.ts` | Middleware frameworks, external webhook routers |
317
+ | Event handler routing | `switch (event.type)` in `server/src/index.ts` or `server/src/handlers/` | Middleware frameworks, external webhook routers |
286
318
  | UI components | `@bkper/web-design` + Lit | Heavy UI frameworks unless the user explicitly requests them |
287
319
 
288
320
  ## Common Pitfalls
@@ -295,19 +327,22 @@ Avoid these patterns even if they seem necessary. The platform or SDK already so
295
327
  2. **Adding `/api/auth/refresh` or similar routes**
296
328
  - Token refresh is internal to `@bkper/web-auth`. Exposing it via Hono routes creates security surface area and duplicates platform functionality.
297
329
 
298
- 3. **Modifying `packages/web/server/` for a simple UI task**
299
- - If the user only asked for a client-side feature, do not touch the server package. The Vite dev server proxies `/api` to the Miniflare worker automatically; you do not need to add routes unless the user explicitly asks for custom backend logic.
330
+ 3. **Relying on browser sessions for server API auth**
331
+ - Sessions let users open app web pages, but `/api/*` routes require `Authorization: Bearer <token>`. Dispatch validates bearer tokens and platform outbound uses that validated context for Bkper API calls.
332
+
333
+ 4. **Modifying `server/` for a simple UI task**
334
+ - If the user only asked for a client-side feature, do not touch server routes. The Vite dev server proxies `/api` to the Miniflare worker automatically; you do not need to add routes unless the user explicitly asks for custom backend logic.
300
335
 
301
- 4. **Installing additional auth or HTTP libraries**
336
+ 5. **Installing additional auth or HTTP libraries**
302
337
  - `bkper-js` and `@bkper/web-auth` are the only packages you need for Bkper API access and authentication. Adding `axios`, `google-auth-library`, or similar is almost always wrong.
303
338
 
304
- 5. **Creating event handlers when the user asked for a UI-only feature**
305
- - If the user says "show me a list of books in a popup," that is a client-only task. Do not scaffold `packages/events/` handlers or subscribe to webhooks.
339
+ 6. **Creating event handlers when the user asked for a UI-only feature**
340
+ - If the user says "show me a list of books in a popup," that is a client-only task. Do not add `/events` logic or subscribe to webhooks.
306
341
 
307
- 6. **Calling REST endpoints directly when `bkper-js` has the method**
342
+ 7. **Calling REST endpoints directly when `bkper-js` has the method**
308
343
  - If `bkper-js` exposes `book.getTransactions()`, use it. Do not `fetch('https://api.bkper.com/...')` and parse JSON manually.
309
344
 
310
- 7. **Reverse-engineering SDK internals**
345
+ 8. **Reverse-engineering SDK internals**
311
346
  - Use the public API surface documented in the API reference. Do not read SDK source to find private methods or internal request patterns.
312
347
 
313
348
  ---
@@ -360,11 +395,8 @@ events:
360
395
  - TRANSACTION_CHECKED
361
396
 
362
397
  deployment:
363
- web:
364
- main: packages/web/server/src/index.ts
365
- client: packages/web/client
366
- events:
367
- main: packages/events/src/index.ts
398
+ server: server/src/index.ts
399
+ client: client
368
400
  services:
369
401
  - KV
370
402
  compatibility_date: '2026-01-28'
@@ -478,9 +510,8 @@ For apps deployed to the [Bkper Platform](https://bkper.com/docs/build/apps/over
478
510
 
479
511
  | Field | Description |
480
512
  | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
481
- | `deployment.web.main` | Entry point for the web handler (serves UI and API). |
482
- | `deployment.web.client` | Directory for static client assets. |
483
- | `deployment.events.main` | Entry point for the events handler (processes webhooks). |
513
+ | `deployment.server` | TypeScript entry point for the single server Worker. It serves `/api/*`, `/events`, and static assets. |
514
+ | `deployment.client` | Optional Vite/static client root. Built assets are deployed with the same Worker. |
484
515
  | `deployment.services` | Platform services to provision. Currently: `KV` (key-value storage). |
485
516
  | `deployment.secrets` | Secret names used by the app. Managed via `bkper app secrets`. |
486
517
  | `deployment.compatibility_date` | [Cloudflare Workers compatibility date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/). |
@@ -574,8 +605,8 @@ source: /docs/build/apps/deploying.md
574
605
  ```
575
606
 
576
607
  This runs two build steps:
577
- - Client (Vite) to static assets in `dist/web/client/`
578
- - Worker bundles (esbuild) — web server to `dist/web/server/`, events handler to `dist/events/`
608
+ - Client (Vite) to static assets in `dist/client/`
609
+ - Server Worker bundle (esbuild) to `dist/server/`
579
610
 
580
611
  Build output includes size reporting so you can monitor bundle sizes.
581
612
 
@@ -621,15 +652,7 @@ Preview URLs use a dash suffix: `https://{appId}-preview.bkper.app`. For example
621
652
 
622
653
  Preview has independent secrets and KV storage from production.
623
654
 
624
- ### Independent handler deployment
625
-
626
- Deploy only the events handler:
627
-
628
- ```bash
629
- bkper app deploy --events
630
- ```
631
-
632
- Useful when you've only changed the events handler and want a faster deployment. Web is deployed by default.
655
+ There is one app deployment per environment. `/events` is handled by the same Worker as the client assets and `/api/*` routes.
633
656
 
634
657
  ## Secrets management
635
658
 
@@ -715,7 +738,7 @@ bkper app install <appId> -b <bookId>
715
738
  bkper app uninstall <appId> -b <bookId>
716
739
  ```
717
740
 
718
- Once installed, the app's [event handlers](https://bkper.com/docs/build/apps/event-handlers.md) receive events from that book, and the app's [context menu](https://bkper.com/docs/build/apps/context-menu.md) appears in the book's UI.
741
+ Once installed, the app's [event handlers](https://bkper.com/docs/build/apps/event-handlers.md) receive events from that book at `/events`, and the app's [context menu](https://bkper.com/docs/build/apps/context-menu.md) appears in the book's UI.
719
742
 
720
743
  ---
721
744
  source: /docs/build/apps/development.md
@@ -734,35 +757,28 @@ The project template runs both processes via `concurrently`:
734
757
 
735
758
  1. **`vite dev`** — Client dev server with HMR. Changes to Lit components reflect instantly in the browser. Configured in `vite.config.ts`.
736
759
  2. **`bkper app dev`** — The worker runtime:
737
- - **Miniflare** — Simulates the Cloudflare Workers runtime locally for the web server and events handler.
738
- - **Cloudflare tunnel** — Exposes the events handler via a public URL so Bkper can route webhook events to your machine.
739
- - **File watching** — Server and shared package changes trigger automatic rebuilds via esbuild.
760
+ - **Miniflare** — Simulates the single Cloudflare Worker locally.
761
+ - **Cloudflare tunnel** — Exposes `/events` via a public URL so Bkper can route webhook events to your machine.
762
+ - **File watching** — Server changes trigger automatic rebuilds via esbuild.
740
763
 
741
- You can also run them independently: `npm run dev:client` for just the UI, or `npm run dev:server` / `npm run dev:events` for specific workers.
764
+ You can also run them independently: `npm run dev:client` for just the UI, or `npm run dev:server` for the local Worker.
742
765
 
743
766
  ## URLs
744
767
 
745
- | Handler | URL |
768
+ | Endpoint | URL |
746
769
  | --- | --- |
747
770
  | Client (Vite dev server) | `http://localhost:5173` |
748
- | Web server (Miniflare) | `http://localhost:8787` |
749
- | Events (via tunnel) | `https://<random>.trycloudflare.com/events` |
771
+ | Server Worker (Miniflare) | `http://localhost:8787` |
772
+ | Events (via tunnel to the same Worker) | `https://<random>.trycloudflare.com/events` |
750
773
 
751
774
  The Vite dev server proxies `/api` requests to `http://localhost:8787` (configured in `vite.config.ts`). The tunnel URL is automatically registered as the `webhookUrlDev` in Bkper, so events from books where you're the developer are routed to your local machine.
752
775
 
753
776
  ## Configuration flags
754
777
 
755
- You can run specific handlers independently:
778
+ There is one local Worker. Override its port when needed:
756
779
 
757
780
  ```bash
758
- # Start only the web worker
759
- bkper app dev --web
760
-
761
- # Start only the events worker
762
- bkper app dev --events
763
-
764
- # Override default ports
765
- bkper app dev --sp 8787 --ep 8791
781
+ bkper app dev --sp 8787
766
782
  ```
767
783
 
768
784
  ## Client configuration
@@ -785,6 +801,8 @@ bkper auth login # one-time setup
785
801
 
786
802
  Then `npm run dev` handles authentication automatically. The client calls `auth.getAccessToken()` and the middleware ensures the token is valid.
787
803
 
804
+ When your client calls an app server route under `/api/*`, include that token as `Authorization: Bearer <token>` to match production dispatch behavior. Local outbound uses your CLI credentials when the app server or event handler calls Bkper.
805
+
788
806
  If you see authentication errors in the browser, verify you're logged in:
789
807
 
790
808
  ```bash
@@ -839,7 +857,7 @@ bkper app build
839
857
  1. Run `npm run dev`
840
858
  2. Edit client code — see changes instantly via Vite HMR
841
859
  3. Edit server code — auto-rebuilds and reloads via esbuild watch
842
- 4. Trigger events in Bkper — your local handler receives them via the tunnel
860
+ 4. Trigger events in Bkper — your local Worker receives them at `/events` via the tunnel
843
861
  5. Check the activity stream in Bkper to see handler responses
844
862
  6. Iterate
845
863
 
@@ -864,7 +882,7 @@ Event handlers are the code that reacts to events in your Bkper Books. When a tr
864
882
  2. Bkper sends an HTTP POST to your webhook URL when those events fire
865
883
  3. Your handler processes the event and returns a response
866
884
 
867
- On the [Bkper Platform](https://bkper.com/docs/build/apps/overview.md), events are routed to your `events` package automatically — including local development via tunnels. For [self-hosted](https://bkper.com/docs/build/apps/self-hosted.md) setups, you configure the webhook URL directly.
885
+ On the [Bkper Platform](https://bkper.com/docs/build/apps/overview.md), events are routed to `/events` on your app's single Worker — including local development via tunnels. For [self-hosted](https://bkper.com/docs/build/apps/self-hosted.md) setups, you configure the webhook URL directly.
868
886
 
869
887
  ## Agent identity
870
888
 
@@ -940,45 +958,36 @@ This pattern is essential for any handler that writes back to the same book.
940
958
 
941
959
  ## Authentication
942
960
 
943
- When Bkper calls your event handler's webhook URL, it sends two headers on every request:
944
-
945
- - `bkper-oauth-token` — The OAuth access token of the user who installed the app. Use this to call the API back on behalf of the user.
946
- - `bkper-agent-id` — Your app's agent identifier.
947
-
948
- Pass these directly to `bkper-js`:
961
+ Platform-hosted event handlers use the same server-side Bkper API pattern as `/api/*` routes:
949
962
 
950
963
  ```ts
951
- const bkper = new Bkper({
952
- oauthTokenProvider: async () => c.req.header('bkper-oauth-token'),
953
- agentIdProvider: async () => c.req.header('bkper-agent-id'),
954
- });
964
+ const bkper = new Bkper();
955
965
  const book = new Book(event.book, bkper.getConfig());
956
966
  ```
957
967
 
958
- This is the canonical pattern. Do not implement custom authentication for event handlers.
968
+ Dispatch consumes the event delivery token, strips platform headers before your Worker runs, and platform outbound auth injects the OAuth token and app agent identity on Bkper API calls.
969
+
970
+ Do not read `bkper-oauth-token`, `bkper-agent-id`, or `Authorization` headers in platform app code.
959
971
 
960
972
  > **Note**
961
- > Both production (`webhookUrl`) and development (`webhookUrlDev`) endpoints receive OAuth tokens in the `bkper-oauth-token` header. During local development, events are routed through the Cloudflare tunnel started by `bkper app dev`.
962
- For [self-hosted](https://bkper.com/docs/build/apps/self-hosted.md) setups, the same headers are sent to your production `webhookUrl`.
973
+ > During local development, events are routed through the Cloudflare tunnel started by `bkper app dev`. Local outbound uses your CLI credentials when the handler calls Bkper.
974
+ For [self-hosted](https://bkper.com/docs/build/apps/self-hosted.md) setups, the event auth headers are sent to both `webhookUrl` and `webhookUrlDev` and must be handled directly by your infrastructure.
963
975
 
964
976
  ## Event routing pattern
965
977
 
966
- On the Bkper Platform, the `events` package uses [Hono](https://hono.dev) to receive webhook calls. A typical pattern routes events by type:
978
+ On the Bkper Platform, your server Worker uses [Hono](https://hono.dev) to receive webhook calls at `/events`. A typical pattern routes events by type:
967
979
 
968
980
  ```ts
969
981
  import { Bkper, Book } from 'bkper-js';
970
982
 
971
- app.post('/', async c => {
983
+ app.post('/events', async c => {
972
984
  const event: bkper.Event = await c.req.json();
973
985
 
974
986
  if (!event.book) {
975
987
  return c.json({ error: 'Missing book in event payload' }, 400);
976
988
  }
977
989
 
978
- const bkper = new Bkper({
979
- oauthTokenProvider: async () => c.req.header('bkper-oauth-token'),
980
- agentIdProvider: async () => c.req.header('bkper-agent-id'),
981
- });
990
+ const bkper = new Bkper();
982
991
  const book = new Book(event.book, bkper.getConfig());
983
992
 
984
993
  switch (event.type) {
@@ -1150,11 +1159,11 @@ This tutorial walks you through building and deploying a Bkper app from scratch.
1150
1159
 
1151
1160
  5. **Make a change**
1152
1161
 
1153
- Edit the handler in `packages/events/src/handlers/transaction-checked.ts` and save. The worker reloads automatically. Check another transaction to see your change.
1162
+ Edit the handler in `server/src/handlers/transaction-checked.ts` and save. The worker reloads automatically. Check another transaction to see your change.
1154
1163
 
1155
1164
  6. **Customize your listing**
1156
1165
 
1157
- Update `bkper.yaml` with your app's description, owner details, and repository URL. Replace the placeholder logos in `packages/web/client/public/images/`. See [App Listing](https://bkper.com/docs/build/apps/app-listing.md) for publishing details.
1166
+ Update `bkper.yaml` with your app's description, owner details, and repository URL. Replace the placeholder logos in `client/public/images/`. See [App Listing](https://bkper.com/docs/build/apps/app-listing.md) for publishing details.
1158
1167
 
1159
1168
  7. **Update the README**
1160
1169
 
@@ -1180,7 +1189,7 @@ This tutorial walks you through building and deploying a Bkper app from scratch.
1180
1189
 
1181
1190
  ## Next steps
1182
1191
 
1183
- - [App Architecture](https://bkper.com/docs/build/apps/architecture.md) — Understand the three-package pattern
1192
+ - [App Architecture](https://bkper.com/docs/build/apps/architecture.md) — Understand the single Worker client/server structure
1184
1193
  - [App Configuration](https://bkper.com/docs/build/apps/configuration.md) — Full `bkper.yaml` reference
1185
1194
  - [Event Handlers](https://bkper.com/docs/build/apps/event-handlers.md) — All event types and patterns
1186
1195
  - [Building & Deploying](https://bkper.com/docs/build/apps/deploying.md) — Preview environments and secrets
@@ -1202,8 +1211,9 @@ Preview environments are built in — deploy to a preview URL to test before goi
1202
1211
 
1203
1212
  OAuth is pre-configured. No client IDs, no redirect URIs, no consent screens to build.
1204
1213
 
1205
- - **Web client** — Use `@bkper/web-auth`: `auth.getAccessToken()`. See [App Architecture → Web client authentication](https://bkper.com/docs/build/apps/architecture.md#web-client-authentication).
1206
- - **Event handlers** — The user's OAuth token arrives in the `bkper-oauth-token` header. See [Event HandlersAuthentication](https://bkper.com/docs/build/apps/event-handlers.md#authentication).
1214
+ - **Web client** — Use `@bkper/web-auth`: `auth.getAccessToken()`. See [App Architecture → Client authentication](https://bkper.com/docs/build/apps/architecture.md#client-authentication).
1215
+ - **Server API routes** — Send `Authorization: Bearer <token>` to `/api/*`; dispatch validates it and platform outbound injects auth for server-side Bkper API calls. See [App ArchitectureServer API authentication](https://bkper.com/docs/build/apps/architecture.md#server-api-authentication).
1216
+ - **Event handlers** — Handle `/events` in the same Worker and call Bkper with server-side `new Bkper()`; dispatch/outbound handle auth and agent identity. See [Event Handlers → Authentication](https://bkper.com/docs/build/apps/event-handlers.md#authentication).
1207
1217
  - **Local development** — The Vite auth middleware uses your CLI credentials. See [Development Experience → Local development authentication](https://bkper.com/docs/build/apps/development.md#local-development-authentication).
1208
1218
 
1209
1219
  ### Services
@@ -1221,7 +1231,7 @@ The project template composes the full development environment:
1221
1231
  npm run dev
1222
1232
  ```
1223
1233
 
1224
- This runs two processes concurrently: `vite dev` for the client UI (HMR), and `bkper app dev` for the worker runtime (Miniflare for your server and event handlers, plus a Cloudflare tunnel so Bkper can route webhook events to your laptop). Your entire development environment, running locally.
1234
+ This runs two processes concurrently: `vite dev` for the client UI (HMR), and `bkper app dev` for the Worker runtime (Miniflare for `/api/*` and `/events`, plus a Cloudflare tunnel so Bkper can route webhook events to your laptop). Your entire development environment, running locally.
1225
1235
 
1226
1236
  ### Deployment
1227
1237
 
@@ -1260,7 +1270,7 @@ bkper app init my-app
1260
1270
  npm run dev
1261
1271
  ```
1262
1272
 
1263
- This gives you a working app with a client UI, server API, and event handler — all running locally with full HMR and webhook tunneling.
1273
+ This gives you a working app with a client UI, server API routes, and `/events` handling in one Worker — all running locally with full HMR and webhook tunneling.
1264
1274
 
1265
1275
  See [Your First App](https://bkper.com/docs/build/apps/first-app.md) for a complete walkthrough, or continue to [App Architecture](https://bkper.com/docs/build/apps/architecture.md) to understand how platform apps are structured.
1266
1276