@buildbase/sdk 0.0.51 → 0.0.54

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 (50) hide show
  1. package/AGENTS.md +24 -8
  2. package/CHANGELOG.md +102 -0
  3. package/README.md +283 -33
  4. package/dist/css/styles.css +1 -1
  5. package/dist/index.cjs +1 -1
  6. package/dist/index.d.ts +1248 -76
  7. package/dist/index.mjs +1 -1
  8. package/dist/mcp/index.cjs +1 -0
  9. package/dist/mcp/index.d.ts +1956 -0
  10. package/dist/mcp/index.mjs +1 -0
  11. package/dist/react/chunks/{SettingsDialog-DitAiAqr.cjs → SettingsDialog-B28WJsAr.cjs} +1 -1
  12. package/dist/react/chunks/{SettingsDialog-uVKFxlal.mjs → SettingsDialog-blhppbNm.mjs} +1 -1
  13. package/dist/react/chunks/{SubscriptionDialog-P0GlXEyn.mjs → SubscriptionDialog-BTWVX3cH.mjs} +1 -1
  14. package/dist/react/chunks/{SubscriptionDialog-Cg-qYVKr.cjs → SubscriptionDialog-QKmwvJU1.cjs} +1 -1
  15. package/dist/react/chunks/ar-1Z1MOGAP.mjs +2 -0
  16. package/dist/react/chunks/ar-VGzBv3AZ.cjs +2 -0
  17. package/dist/react/chunks/de-BjdDg60Y.cjs +2 -0
  18. package/dist/react/chunks/de-CXUItplO.mjs +2 -0
  19. package/dist/react/chunks/es-BBPsSZUp.cjs +2 -0
  20. package/dist/react/chunks/es-BTbzVutz.mjs +2 -0
  21. package/dist/react/chunks/fr-Cax48X_S.cjs +2 -0
  22. package/dist/react/chunks/fr-DPMeOQu3.mjs +2 -0
  23. package/dist/react/chunks/hi-BJqvpG6d.cjs +2 -0
  24. package/dist/react/chunks/hi-JfJx-LV7.mjs +2 -0
  25. package/dist/react/chunks/ja-CDGtLzTk.mjs +2 -0
  26. package/dist/react/chunks/ja-DvVyj5xX.cjs +2 -0
  27. package/dist/react/chunks/react-6QJCOE-D.cjs +2 -0
  28. package/dist/react/chunks/react-CC6T7Zky.mjs +2 -0
  29. package/dist/react/chunks/zh-BAcqxkxk.cjs +2 -0
  30. package/dist/react/chunks/zh-BNZ2Ooe8.mjs +2 -0
  31. package/dist/react/index.cjs +1 -1
  32. package/dist/react/index.d.ts +98 -43
  33. package/dist/react/index.mjs +1 -1
  34. package/package.json +20 -5
  35. package/dist/react/chunks/ar-SrPOJXdL.mjs +0 -2
  36. package/dist/react/chunks/ar-a3aRbPCs.cjs +0 -2
  37. package/dist/react/chunks/de-DE3_z7r0.cjs +0 -2
  38. package/dist/react/chunks/de-xnPl61SS.mjs +0 -2
  39. package/dist/react/chunks/es-C_xcyGtp.cjs +0 -2
  40. package/dist/react/chunks/es-CyuBtosP.mjs +0 -2
  41. package/dist/react/chunks/fr-BMu7Bk4k.cjs +0 -2
  42. package/dist/react/chunks/fr-CxuDLZ27.mjs +0 -2
  43. package/dist/react/chunks/hi-CQ215yc9.cjs +0 -2
  44. package/dist/react/chunks/hi-CpuvyorS.mjs +0 -2
  45. package/dist/react/chunks/ja-DIJ_xCIi.mjs +0 -2
  46. package/dist/react/chunks/ja-DK6Buuws.cjs +0 -2
  47. package/dist/react/chunks/react-C2ikFWBG.cjs +0 -2
  48. package/dist/react/chunks/react-CCukNQ4q.mjs +0 -2
  49. package/dist/react/chunks/zh-CYQwZiBL.mjs +0 -2
  50. package/dist/react/chunks/zh-Dt-apS2s.cjs +0 -2
@@ -0,0 +1,1956 @@
1
+ import { z } from 'zod';
2
+
3
+ /** An Agent Skill (Agent Skills Discovery RFC v0.2.0) published by this app. */
4
+ interface AgentSkill {
5
+ /** Stable slug, e.g. `imejis-api`. */
6
+ name: string;
7
+ /** One-line human/agent-readable summary. */
8
+ description: string;
9
+ /** Full markdown content of the SKILL.md served for this skill. */
10
+ content: string;
11
+ /**
12
+ * Path the SKILL.md is served from. Defaults to
13
+ * `/.well-known/agent-skills/<name>/SKILL.md`.
14
+ */
15
+ path?: string;
16
+ /** Skill artifact kind. Defaults to `skill-md`. */
17
+ type?: 'skill-md' | 'archive';
18
+ }
19
+ /** One API described in an API Catalog (RFC 9727 / RFC 9264 linkset). */
20
+ interface ApiCatalogApi {
21
+ /** Anchor URL identifying the API, e.g. `https://api.imejis.io`. */
22
+ anchor: string;
23
+ /** OpenAPI / machine-readable description URL (link rel `service-desc`). */
24
+ serviceDesc?: string;
25
+ /** Human documentation URL (link rel `service-doc`). */
26
+ serviceDoc?: string;
27
+ /** Health / status endpoint URL (link rel `status`). */
28
+ status?: string;
29
+ /** Optional human title for the API. */
30
+ title?: string;
31
+ }
32
+ /** MCP Server Card (SEP-1649 v1.0), advertising a live MCP server. */
33
+ interface McpServerCard {
34
+ /** Server name. */
35
+ name: string;
36
+ /** Server version. */
37
+ version: string;
38
+ /** MCP transport endpoint URL the agent connects to. */
39
+ endpoint: string;
40
+ /** Transport type. Defaults to `"streamable-http"`. */
41
+ transport?: string;
42
+ /** MCP protocol version the server speaks. Defaults to `"2025-06-18"`. */
43
+ protocolVersion?: string;
44
+ /**
45
+ * Capability flags. Booleans per SEP-1649 v1.0; a legacy object value
46
+ * (e.g. `{ tools: {} }`) is normalized to booleans by presence.
47
+ */
48
+ capabilities?: Record<string, unknown>;
49
+ /** Optional human description. */
50
+ description?: string;
51
+ /** Documentation URL. */
52
+ documentationUrl?: string;
53
+ }
54
+ /** One OAuth scope an app declares in its scope catalog. */
55
+ interface AppScope {
56
+ /** Scope name, e.g. `designs:read`. */
57
+ name: string;
58
+ /** Human-readable description, shown on the consent screen. */
59
+ description: string;
60
+ }
61
+ /** One skill advertised on the A2A Agent Card. */
62
+ interface A2ACardSkill {
63
+ id: string;
64
+ name: string;
65
+ description: string;
66
+ tags?: string[];
67
+ }
68
+ /**
69
+ * Overrides for the A2A Agent Card (`/.well-known/agent-card.json`). Every
70
+ * field is optional — the card is fully derived from `site` + `skills` by
71
+ * default. See {@link buildA2AAgentCard}.
72
+ */
73
+ interface A2ACardConfig {
74
+ name?: string;
75
+ version?: string;
76
+ description?: string;
77
+ /** The endpoint agents connect to. Defaults to `siteUrl`. */
78
+ serviceUrl?: string;
79
+ /** Transport protocol advertised for `serviceUrl`. Defaults to `'http'`. */
80
+ transportProtocol?: string;
81
+ /** A2A capability flags. Defaults to `{ streaming: false, pushNotifications: false }`. */
82
+ capabilities?: Record<string, unknown>;
83
+ /** Skills; defaults to a mapping of `config.skills` (or a generic site skill). */
84
+ skills?: A2ACardSkill[];
85
+ }
86
+ /** One robots.txt policy group (a `User-agent:` block). */
87
+ interface RobotsPolicy {
88
+ /** User-agent(s) this group applies to, e.g. `'*'` or `'GPTBot'`. */
89
+ userAgent: string | string[];
90
+ /** Paths to allow, e.g. `['/']`. */
91
+ allow?: string[];
92
+ /** Paths to disallow, e.g. `['/api/']`. */
93
+ disallow?: string[];
94
+ /** Optional `Crawl-delay` (non-standard but widely honored). */
95
+ crawlDelay?: number;
96
+ }
97
+ /**
98
+ * Content Signals (https://contentsignals.org) — declares how crawlers may use
99
+ * this site's content. Each flag renders as `<signal>=yes|no`; omitted flags
100
+ * are not emitted (unset = no preference expressed).
101
+ */
102
+ interface ContentSignals {
103
+ /** May content be indexed for search? → `search=yes|no`. */
104
+ search?: boolean;
105
+ /** May content be used as AI input (RAG, grounding)? → `ai-input=yes|no`. */
106
+ aiInput?: boolean;
107
+ /** May content be used for AI training? → `ai-train=yes|no`. */
108
+ aiTrain?: boolean;
109
+ }
110
+ /** Configuration for `buildRobotsTxt`. Every field is optional. */
111
+ interface RobotsConfig {
112
+ /** Policy groups. Defaults to `[{ userAgent: '*', allow: ['/'] }]`. */
113
+ policies?: RobotsPolicy[];
114
+ /**
115
+ * Stance toward known AI crawlers ({@link AI_BOT_USER_AGENTS}): `'allow'`
116
+ * (default — an explicit `Allow: /` group per bot, so readiness checkers see
117
+ * AI bots addressed), `'deny'` (a `Disallow: /` group per bot), or explicit
118
+ * per-bot policies for finer control.
119
+ */
120
+ aiBots?: 'allow' | 'deny' | RobotsPolicy[];
121
+ /** Content-Signal directive attached to the `User-agent: *` group. */
122
+ contentSignals?: ContentSignals;
123
+ /**
124
+ * Sitemap URLs. Defaults to `${siteUrl}/sitemap.xml` when `config.sitemap`
125
+ * is set; pass `[]` to emit no `Sitemap:` line.
126
+ */
127
+ sitemaps?: string[];
128
+ }
129
+ /** One URL entry for the generated sitemap.xml. */
130
+ interface SitemapUrl {
131
+ /** Absolute URL, or a path resolved against `siteUrl`. */
132
+ loc: string;
133
+ /** ISO-8601 last-modified date. */
134
+ lastmod?: string;
135
+ changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
136
+ /** 0.0–1.0. */
137
+ priority?: number;
138
+ }
139
+ /** Configuration for the agent-readiness discovery layer. */
140
+ interface AgentReadyConfig {
141
+ /** BuildBase server base URL, e.g. `https://api.buildbase.app` (no trailing slash needed). */
142
+ serverUrl: string;
143
+ /** The org id (24-hex) whose agent-readiness config to serve. */
144
+ orgId: string;
145
+ /**
146
+ * This app's public origin, e.g. `https://imejis.io`. Used to build absolute
147
+ * URLs in the Agent Card and to match RFC 9728 resource paths.
148
+ */
149
+ siteUrl: string;
150
+ /** Static metadata for the Agent Card (`/.well-known/agent.json`). */
151
+ site: {
152
+ name: string;
153
+ description?: string;
154
+ /** Absolute URL to human/agent documentation. */
155
+ documentationUrl?: string;
156
+ /** Support/contact email surfaced on the Agent Card. */
157
+ contactEmail?: string;
158
+ /** Provider block; defaults to `{ name: site.name, url: siteUrl }`. */
159
+ provider?: {
160
+ name: string;
161
+ url: string;
162
+ };
163
+ };
164
+ /** Agent Skills to publish. Omit to skip the Agent Skills documents. */
165
+ skills?: AgentSkill[];
166
+ /**
167
+ * The app's OAuth scope catalog — the source of truth for `scopes_supported`
168
+ * in this app's RFC 9728 protected-resource metadata (served from your own
169
+ * origin). Declare real, per-app scopes here (`designs:read`,
170
+ * `render:execute`, …) instead of placeholders. A `protectedResources` entry
171
+ * without its own `scopes` list inherits every catalog scope name. Scopes are
172
+ * app-owned: the shared BuildBase authorization server stays scope-agnostic.
173
+ */
174
+ scopes?: AppScope[];
175
+ /** security.txt fields (RFC 9116). Omit to skip serving security.txt. */
176
+ security?: {
177
+ /** Contact URI(s) — email (`mailto:`) or URL. */
178
+ contact: string | string[];
179
+ /** ISO-8601 expiry; strongly recommended by RFC 9116. */
180
+ expires?: string;
181
+ /** Security policy URL. */
182
+ policy?: string;
183
+ };
184
+ /**
185
+ * API Catalog entries (RFC 9727), served at `/.well-known/api-catalog` as
186
+ * `application/linkset+json`. Omit to skip.
187
+ */
188
+ apiCatalog?: ApiCatalogApi[];
189
+ /**
190
+ * MCP Server Card (SEP-1649), served at `/.well-known/mcp/server-card.json`.
191
+ * Only set this when you actually run an MCP server — the card advertises a
192
+ * live transport endpoint. Omit to skip.
193
+ */
194
+ mcpServerCard?: McpServerCard;
195
+ /**
196
+ * Raw `/auth.md` content (agent registration/auth instructions). If omitted,
197
+ * `buildAuthMd` generates one from the OAuth metadata + site config.
198
+ */
199
+ authMd?: string;
200
+ /**
201
+ * Local `/llms.txt` content. Takes precedence over the org-authored llms.txt
202
+ * from the platform bundle — use when the app owns richer llms.txt content.
203
+ */
204
+ llmsTxt?: string;
205
+ /**
206
+ * Local `/llms-full.txt` content (llmstxt.org expanded document). Omit to
207
+ * skip serving it (or serve your own static file, which then wins).
208
+ */
209
+ llmsFullTxt?: string;
210
+ /**
211
+ * A2A Agent Card served at `/.well-known/agent-card.json`. Served by
212
+ * DEFAULT, derived from `site` + `skills`, so readiness scanners find a
213
+ * valid card with zero config. Pass `false` to disable, or an object to
214
+ * override any derived field.
215
+ */
216
+ a2aCard?: false | A2ACardConfig;
217
+ /**
218
+ * Web Bot Auth key directory (IETF WebBotAuth) served at
219
+ * `/.well-known/http-message-signatures-directory`. Only for apps whose own
220
+ * outbound bot/agent signs its requests — publish the JWKS public keys here
221
+ * so receiving sites can verify the signatures. Omitted = not served.
222
+ */
223
+ webBotAuth?: {
224
+ keys: Array<Record<string, unknown>>;
225
+ };
226
+ /**
227
+ * Extra literal documents served by {@link resolveAgentPath}, keyed by exact
228
+ * request path. The escape hatch for anything the SDK has no builder for —
229
+ * commerce discovery (x402, UCP, ACP, MPP), `/openapi.json`, future
230
+ * `.well-known` documents. A string value infers its content type from the
231
+ * path extension. Extra paths take precedence over built-in documents, so
232
+ * they can also override any SDK-generated document.
233
+ */
234
+ extraPaths?: Record<string, string | {
235
+ body: string;
236
+ contentType?: string;
237
+ status?: number;
238
+ cacheControl?: string;
239
+ }>;
240
+ /** robots.txt generation (`buildRobotsTxt`). Omitted = sane defaults. */
241
+ robots?: RobotsConfig;
242
+ /**
243
+ * URLs for a minimal generated sitemap.xml (`buildSitemap`). Omit when the
244
+ * app already generates sitemaps (e.g. next-sitemap) — the builder then
245
+ * returns null and your own files win.
246
+ */
247
+ sitemap?: {
248
+ urls: Array<string | SitemapUrl>;
249
+ };
250
+ /**
251
+ * RFC 9728 protected resources this app serves, defined locally (app-owned).
252
+ * When set, the SDK builds the `/.well-known/oauth-protected-resource`
253
+ * document from this config — `authorization_servers` is derived from
254
+ * `serverUrl`/`orgId` (the BuildBase issuer). Takes precedence over the
255
+ * platform bundle. Omit to fall back to the org-configured bundle.
256
+ */
257
+ protectedResources?: Array<{
258
+ /** The protected API's resource URI. Defaults to `siteUrl`. */
259
+ resource?: string;
260
+ /** Scopes this resource accepts (`scopes_supported`). */
261
+ scopes?: string[];
262
+ /** Human documentation URL (`resource_documentation`). */
263
+ documentationUrl?: string;
264
+ /** Bearer methods (`bearer_methods_supported`). Defaults to `['header']`. */
265
+ bearerMethods?: string[];
266
+ }>;
267
+ /** TTL (seconds) for cached platform fetches. Default 300 (5 min). */
268
+ cacheTtlSeconds?: number;
269
+ /**
270
+ * Timeout (ms) for platform fetches. Fail-soft on timeout, so a hung or slow
271
+ * BuildBase server can't hang discovery routes forever. Default 5000; set `0`
272
+ * to disable.
273
+ */
274
+ fetchTimeoutMs?: number;
275
+ /** Injectable fetch, mainly for testing. Defaults to the global `fetch`. */
276
+ fetch?: typeof fetch;
277
+ }
278
+ /** A rendered discovery document ready to become an HTTP response. */
279
+ interface DiscoveryDocument {
280
+ status: number;
281
+ contentType: string;
282
+ body: string;
283
+ cacheControl: string;
284
+ /** Value for a `Vary` response header, when the body is negotiated. */
285
+ vary?: string;
286
+ }
287
+
288
+ /**
289
+ * Built-in MCP tools mapping BuildBase capabilities to agent-callable tools.
290
+ *
291
+ * Authorization model: the agent acts as an authenticated app user — it knows
292
+ * your app, not BuildBase. Built-in tools therefore carry NO BuildBase-specific
293
+ * scope requirement; they're available whenever your `auth.verify` accepts the
294
+ * token and your app chose to expose them. Every call runs under the acting
295
+ * user's own BuildBase session, so the server enforces that user's permissions
296
+ * — an agent can never exceed what its user could do. Want per-scope gating on
297
+ * top? Set `requiredScopes` on your own custom tools — the handler enforces it.
298
+ *
299
+ * The read-vs-write boundary is YOUR choice via `builtinTools`: the default is
300
+ * `'readonly'` (reads only — least privilege). Opt in to `'all'` to also expose
301
+ * writes AND destructive/billing ops (workspace delete, subscription cancel,
302
+ * credit purchase — still gated by the user's own permissions), `false` to
303
+ * expose no built-ins, or `{ include, exclude }` to hand-pick specific tools.
304
+ *
305
+ * Every workspace-scoped tool accepts an optional `workspaceId` and falls back
306
+ * to the workspace pinned on the verified token (`McpAuthInfo.workspaceId`).
307
+ */
308
+
309
+ /** Every built-in tool, keyed by name. */
310
+ declare const builtinMcpTools: ReadonlyMap<string, McpToolDefinition<any>>;
311
+ /** The name of a built-in BuildBase MCP tool. */
312
+ type BuiltinMcpToolName = 'list_workspaces' | 'get_workspace' | 'get_user_profile' | 'list_workspace_users' | 'get_subscription' | 'get_plans' | 'get_plan_versions' | 'get_public_plans' | 'get_plan_version' | 'list_invoices' | 'get_invoice' | 'get_quota_usage' | 'get_all_quota_usage' | 'get_usage_logs' | 'get_credit_balance' | 'list_credit_transactions' | 'get_credit_packages' | 'get_credit_buckets' | 'get_expiring_credits' | 'get_public_credit_packages' | 'check_feature_flag' | 'check_permission' | 'resolve_permissions' | 'get_settings' | 'record_usage' | 'record_usage_batch' | 'consume_credits' | 'send_notification' | 'create_workspace' | 'update_workspace' | 'delete_workspace' | 'invite_workspace_user' | 'remove_workspace_user' | 'update_workspace_user_role' | 'update_user_profile' | 'update_feature_flag' | 'create_subscription_checkout' | 'update_subscription' | 'cancel_subscription' | 'resume_subscription' | 'get_billing_portal_url' | 'purchase_credits';
313
+ /** Resolve a `builtinTools` config value to the tools it selects. */
314
+ declare function selectBuiltinTools(selection: 'all' | 'readonly' | false | {
315
+ include?: BuiltinMcpToolName[];
316
+ exclude?: BuiltinMcpToolName[];
317
+ }): McpToolDefinition<any>[];
318
+
319
+ interface IDocument {
320
+ _id: string;
321
+ /** Optional; some APIs only return _id. Prefer _id for identity. */
322
+ id?: string;
323
+ createdAt: string;
324
+ updatedAt: string;
325
+ deleted: boolean;
326
+ }
327
+ interface IUser extends IDocument {
328
+ _id: string;
329
+ name: string;
330
+ email: string;
331
+ /** Profile image URL. Often omitted or empty from API. */
332
+ image?: string;
333
+ role: string;
334
+ /** ISO country code. May be omitted. */
335
+ country?: string;
336
+ /** IANA timezone. May be omitted. */
337
+ timezone?: string;
338
+ /** Language code. May be omitted. */
339
+ language?: string;
340
+ /** Currency code. May be omitted. */
341
+ currency?: string;
342
+ attributes?: Record<string, string | number | boolean>;
343
+ }
344
+ type BillingInterval = 'monthly' | 'yearly' | 'quarterly';
345
+ /** Subscription status constants */
346
+ declare const SubscriptionStatus: {
347
+ readonly Active: "active";
348
+ readonly Trialing: "trialing";
349
+ readonly Canceled: "canceled";
350
+ readonly PastDue: "past_due";
351
+ readonly Paused: "paused";
352
+ readonly Incomplete: "incomplete";
353
+ readonly Unpaid: "unpaid";
354
+ };
355
+ type SubscriptionStatusType = (typeof SubscriptionStatus)[keyof typeof SubscriptionStatus];
356
+ interface ISubscription {
357
+ _id: string;
358
+ subscriptionStatus: SubscriptionStatusType;
359
+ /** Stripe Subscription ID (e.g. 'sub_...'). Present when linked to Stripe. */
360
+ subscriptionId?: string;
361
+ stripePriceId?: string;
362
+ stripeCurrentPeriodEnd?: string;
363
+ /** When the current monthly usage period ends (quotas reset on this date). */
364
+ usageCurrentPeriodEnd?: string;
365
+ cancelAtPeriodEnd: boolean;
366
+ billingInterval?: BillingInterval;
367
+ /** Trial start date (ISO string). Set when subscription is in trial. */
368
+ trialStart?: string;
369
+ /** Trial end date (ISO string). Set when subscription is in trial. */
370
+ trialEnd?: string;
371
+ /** When the subscription was canceled (ISO string). Soft-delete marker. */
372
+ canceledAt?: string;
373
+ /** Dunning state: 'none', 'notified', 'warning', 'final', 'suspended'. */
374
+ dunningState?: string;
375
+ /** Whether this subscription uses per-seat pricing. */
376
+ seatPricingEnabled?: boolean;
377
+ /** Current billable seat count. */
378
+ currentSeatCount?: number;
379
+ createdAt: string;
380
+ updatedAt: string;
381
+ plan?: {
382
+ _id: string;
383
+ name: string;
384
+ slug: string;
385
+ description?: string;
386
+ /** Stripe currency code (e.g. 'usd', 'inr', 'eur'). */
387
+ currency?: string;
388
+ };
389
+ }
390
+ interface ISubscriptionItem {
391
+ _id: string;
392
+ type: 'feature' | 'limit' | 'quota';
393
+ name: string;
394
+ slug: string;
395
+ description?: string;
396
+ category: string;
397
+ }
398
+ /** Per-interval quota value (plan-group/versions and public plans schema). */
399
+ interface IQuotaIntervalValue {
400
+ included: number;
401
+ /** Overage price in cents. Omitted in public plans; use pricingVariant.quotaOverages for display. */
402
+ overage?: number;
403
+ /** Stripe price ID for overage. Omitted in public plans; use pricingVariant.quotaOveragePriceIds. */
404
+ priceId?: string;
405
+ /** Unit size for overage pricing (e.g. 1000 = price per 1000 units). */
406
+ unitSize?: number;
407
+ }
408
+ /** Quota defined per billing interval (new plan-group/versions schema) */
409
+ interface IQuotaByInterval {
410
+ /** Whether over-usage beyond included is allowed (billed as overage). When false, usage is hard-capped at included amount. Default true. */
411
+ allowOverage?: boolean;
412
+ monthly?: IQuotaIntervalValue;
413
+ yearly?: IQuotaIntervalValue;
414
+ quarterly?: IQuotaIntervalValue;
415
+ }
416
+ /** Base pricing per interval. Values are often in cents (Stripe); convert for display. */
417
+ interface IBasePricing {
418
+ monthly: number;
419
+ yearly: number;
420
+ quarterly: number;
421
+ }
422
+ /** Stripe price IDs per billing interval (for a single currency variant). */
423
+ interface IStripePricesByInterval {
424
+ monthlyPriceId?: string;
425
+ yearlyPriceId?: string;
426
+ quarterlyPriceId?: string;
427
+ monthly?: string;
428
+ yearly?: string;
429
+ }
430
+ /** Overage amounts in cents per interval, keyed by quota slug. Used in pricing variants. */
431
+ type IQuotaOveragesByInterval = Record<string, {
432
+ monthly?: number;
433
+ yearly?: number;
434
+ quarterly?: number;
435
+ }>;
436
+ /** Overage Stripe price IDs per interval, keyed by quota slug. Used in pricing variants. */
437
+ type IQuotaOveragePriceIdsByInterval = Record<string, {
438
+ monthly?: string;
439
+ yearly?: string;
440
+ quarterly?: string;
441
+ }>;
442
+ /**
443
+ * Multi-currency pricing variant at plan version level.
444
+ * Each plan version can have multiple variants (e.g. usd, eur, inr, sgd).
445
+ */
446
+ interface IPricingVariant {
447
+ _id: string;
448
+ currency: string;
449
+ basePricing: IBasePricing;
450
+ stripePrices: IStripePricesByInterval;
451
+ /** Per-seat pricing config. When enabled, a second recurring item is on the subscription. */
452
+ seatPricing?: {
453
+ enabled: boolean;
454
+ includedSeats: number;
455
+ /** Maximum seats allowed. 0 or undefined = unlimited. */
456
+ maxSeats?: number;
457
+ perSeat: IBasePricing;
458
+ };
459
+ /** Stripe Price IDs for the per-seat recurring price. */
460
+ seatStripePrices?: IStripePricesByInterval;
461
+ /** Overage cents per quota slug per interval. */
462
+ quotaOverages?: IQuotaOveragesByInterval;
463
+ /** Stripe price IDs for overage per quota slug per interval. */
464
+ quotaOveragePriceIds?: IQuotaOveragePriceIdsByInterval;
465
+ }
466
+ interface IPlanVersion {
467
+ _id: string;
468
+ version: number;
469
+ name: string;
470
+ status: 'draft' | 'published';
471
+ features?: Record<string, boolean>;
472
+ limits?: Record<string, number>;
473
+ /** Per-interval quotas (included, unitSize; overage from pricingVariant.quotaOverages). */
474
+ quotas?: Record<string, IQuotaByInterval>;
475
+ /** Multi-currency pricing. Each variant has currency, basePricing, stripePrices, quotaOverages. */
476
+ pricingVariants?: IPricingVariant[];
477
+ subscriptionItems?: ISubscriptionItem[];
478
+ /** Trial configuration. When enabled, users can start a free trial of this plan. */
479
+ trial?: {
480
+ enabled: boolean;
481
+ durationDays: number;
482
+ requireCard: boolean;
483
+ };
484
+ /** Credit grant configuration. When enabled, credits are granted to workspaces on this plan. */
485
+ creditGrant?: {
486
+ enabled: boolean;
487
+ /** Linked credit package (populated with name, slug, creditAmount). */
488
+ creditPackage?: {
489
+ _id: string;
490
+ name: string;
491
+ slug: string;
492
+ creditAmount: number;
493
+ } | string;
494
+ /** How unused plan credits are handled on renewal: 'reset' = expire old, 'topup' = add to existing. */
495
+ mode: 'reset' | 'topup';
496
+ /** true = credits given fresh every month, false = one-time lifetime grant (default). */
497
+ renewOnPeriod?: boolean;
498
+ };
499
+ isCurrent?: boolean;
500
+ isLegacy?: boolean;
501
+ archived?: boolean;
502
+ deleted?: boolean;
503
+ createdAt?: string;
504
+ updatedAt?: string;
505
+ id?: string;
506
+ stripeProductId?: string;
507
+ }
508
+ /** Plan version summary (e.g. plan.latestVersion from subscription API) with subscriptionItems as IDs. */
509
+ interface IPlanVersionSummary extends Omit<IPlanVersion, 'subscriptionItems'> {
510
+ subscriptionItems?: string[];
511
+ }
512
+ interface IPlan {
513
+ _id: string;
514
+ name: string;
515
+ slug: string;
516
+ description?: string;
517
+ /** Stripe currency code (e.g. 'usd', 'inr', 'eur'). Used for pricing display. */
518
+ currency?: string;
519
+ /** Freemium plan: $0 base pricing, no seat billing, no trial. */
520
+ isFreemium?: boolean;
521
+ latestVersion?: IPlanVersionSummary;
522
+ archived?: boolean;
523
+ deleted?: boolean;
524
+ createdAt?: string;
525
+ updatedAt?: string;
526
+ id?: string;
527
+ }
528
+ interface IPlanGroupLatestVersion {
529
+ _id: string;
530
+ version: number;
531
+ planVersionIds: string[];
532
+ requiredItems: string[];
533
+ status: 'draft' | 'published';
534
+ isCurrent?: boolean;
535
+ isLegacy?: boolean;
536
+ archived?: boolean;
537
+ deleted?: boolean;
538
+ createdAt?: string;
539
+ updatedAt?: string;
540
+ description?: string;
541
+ }
542
+ interface IPlanGroup {
543
+ _id: string;
544
+ name: string;
545
+ slug: string;
546
+ description?: string;
547
+ latestVersion?: IPlanGroupLatestVersion;
548
+ archived?: boolean;
549
+ deleted?: boolean;
550
+ createdAt?: string;
551
+ updatedAt?: string;
552
+ id?: string;
553
+ }
554
+ interface IPlanGroupVersion {
555
+ _id: string;
556
+ version: number;
557
+ description?: string;
558
+ group?: {
559
+ _id: string;
560
+ name: string;
561
+ description?: string;
562
+ };
563
+ planVersionIds: IPlanVersionWithPlan[] | string[];
564
+ requiredItems?: string[];
565
+ status?: 'draft' | 'published';
566
+ isCurrent?: boolean;
567
+ isLegacy?: boolean;
568
+ archived?: boolean;
569
+ deleted?: boolean;
570
+ createdAt?: string;
571
+ updatedAt?: string;
572
+ id?: string;
573
+ }
574
+ interface ISubscriptionResponse {
575
+ subscription: ISubscription | null;
576
+ planVersion: IPlanVersion | null;
577
+ plan: IPlan | null;
578
+ group: IPlanGroup | null;
579
+ groupVersion: IPlanGroupVersion | null;
580
+ }
581
+ /** Plan version with nested plan info (name, slug, currency for display). */
582
+ interface IPlanVersionWithPlan extends IPlanVersion {
583
+ plan: IPlan;
584
+ }
585
+ /**
586
+ * Plan group version with full plan versions (for GET .../plan-group/versions).
587
+ * Each plan has plan, subscriptionItems, quotas (per-interval), pricingVariants.
588
+ */
589
+ interface IPlanGroupVersionWithPlans {
590
+ _id: string;
591
+ version: number;
592
+ description?: string;
593
+ /** Full plan versions with nested plan (name, slug, currency). */
594
+ plans: IPlanVersionWithPlan[];
595
+ isCurrent?: boolean;
596
+ whatsNew?: {
597
+ newPlans: IPlanVersionWithPlan[];
598
+ updatedPlans: IPlanVersionWithPlan[];
599
+ removedPlans: IPlanVersionWithPlan[];
600
+ };
601
+ }
602
+ interface IPlanGroupInfo {
603
+ _id: string;
604
+ name: string;
605
+ slug: string;
606
+ description?: string;
607
+ }
608
+ /** Response from GET workspaces/:id/subscription/plan-group (and by version). */
609
+ interface IPlanGroupResponse {
610
+ /** Plan group with latestVersion, archived, deleted, timestamps. May be null in some API responses. */
611
+ group: IPlanGroup | null;
612
+ /** Current group version with populated planVersionIds (or IDs). */
613
+ groupVersion: IPlanGroupVersion;
614
+ /** Full plan versions for display (subscriptionItems, pricingVariants, quotas). */
615
+ plans: IPlanVersionWithPlan[];
616
+ }
617
+ /**
618
+ * Response from GET workspaces/:workspaceId/subscription/plan-group/versions.
619
+ * Group summary plus current and available group versions; each version has plans with
620
+ * per-interval quotas and pricingVariants.
621
+ */
622
+ interface IPlanGroupVersionsResponse {
623
+ /** Plan group summary (_id, name, slug). */
624
+ group: IPlanGroupInfo;
625
+ /** Current group version (user's version when subscribed, or latest published when not). */
626
+ currentVersion: IPlanGroupVersionWithPlans;
627
+ /** Newer versions when user has subscription; empty when no subscription. */
628
+ availableVersions: IPlanGroupVersionWithPlans[];
629
+ }
630
+ /**
631
+ * Optional Stripe-level overrides forwarded to the checkout session.
632
+ * Use this to pass affiliate/referral tracking data or other Stripe parameters
633
+ * that should be set on the checkout session or resulting subscription.
634
+ */
635
+ interface ICheckoutStripeOptions {
636
+ /** Custom metadata merged into the Stripe checkout session metadata. */
637
+ metadata?: Record<string, string>;
638
+ /**
639
+ * Stripe `client_reference_id` — most affiliate platforms (Rewardful,
640
+ * FirstPromoter, PartnerStack, etc.) read this field to attribute conversions.
641
+ */
642
+ clientReferenceId?: string;
643
+ /** Custom metadata merged into `subscription_data.metadata` on the Stripe session. */
644
+ subscriptionMetadata?: Record<string, string>;
645
+ }
646
+ /** Request body for POST .../workspaces/:id/subscription/checkout. */
647
+ interface ICheckoutSessionRequest {
648
+ planVersionId: string;
649
+ billingInterval?: BillingInterval;
650
+ currency?: string;
651
+ successUrl?: string;
652
+ cancelUrl?: string;
653
+ /** Stripe-level overrides for affiliate tracking, referral IDs, etc. */
654
+ stripeOptions?: ICheckoutStripeOptions;
655
+ }
656
+ interface ICheckoutSessionResponse {
657
+ /** Response type discriminator. Added for centralized checkout flow. */
658
+ type?: 'checkout' | 'trial_started' | 'existing';
659
+ success: boolean;
660
+ checkoutUrl: string;
661
+ sessionId: string;
662
+ message: string;
663
+ planVersion?: {
664
+ _id: string;
665
+ version: number;
666
+ name: string;
667
+ };
668
+ }
669
+ /** No-card trial started server-side; no redirect needed. */
670
+ interface ICheckoutTrialResult {
671
+ type: 'trial_started';
672
+ success: boolean;
673
+ checkoutUrl: null;
674
+ sessionId: null;
675
+ subscription: ISubscription;
676
+ message: string;
677
+ }
678
+ /** Workspace already has a matching active subscription. */
679
+ interface ICheckoutExistingResult {
680
+ type: 'existing';
681
+ success: boolean;
682
+ checkoutUrl: null;
683
+ sessionId: null;
684
+ message: string;
685
+ existingSubscription?: {
686
+ _id: string;
687
+ subscriptionStatus: string;
688
+ stripePriceId?: string;
689
+ };
690
+ }
691
+ /**
692
+ * Discriminated union for POST /subscription/checkout responses.
693
+ * - `checkout`: Stripe checkout session created, redirect user to checkoutUrl
694
+ * - `trial_started`: No-card trial started server-side, no redirect needed
695
+ * - `existing`: Workspace already has a matching subscription
696
+ */
697
+ type CheckoutResult = (ICheckoutSessionResponse & {
698
+ type: 'checkout';
699
+ }) | ICheckoutTrialResult | ICheckoutExistingResult;
700
+ /** Request body for PATCH .../workspaces/:id/subscription. Only these fields are allowed. */
701
+ interface ISubscriptionUpdateRequest {
702
+ planVersionId: string;
703
+ billingInterval?: BillingInterval;
704
+ successUrl?: string;
705
+ cancelUrl?: string;
706
+ }
707
+ interface ISubscriptionUpdateResponse {
708
+ _id: string;
709
+ subscriptionStatus: string;
710
+ workspace: string;
711
+ planVersion: string;
712
+ plan: string;
713
+ planGroup?: string;
714
+ planGroupVersion?: string;
715
+ cancelAtPeriodEnd: boolean;
716
+ createdAt: string;
717
+ updatedAt: string;
718
+ }
719
+ interface IPublicPlanItemCategory {
720
+ name: string;
721
+ slug: string;
722
+ }
723
+ interface IPublicPlanItem {
724
+ _id: string;
725
+ name: string;
726
+ slug: string;
727
+ description?: string;
728
+ type: 'feature' | 'limit' | 'quota';
729
+ category: IPublicPlanItemCategory;
730
+ }
731
+ /**
732
+ * Public plan version (e.g. from GET /api/v1/public/:orgId/plans/:slug).
733
+ * Each plan has _id, name (plan display name), version, status, pricingVariants, quotas, features, limits.
734
+ * Pricing is in cents (see response.notes).
735
+ */
736
+ interface IPublicPlanVersion {
737
+ _id: string;
738
+ /** Plan display name (e.g. "Pro", "Base Plan"). */
739
+ name: string;
740
+ /** Whether this is a free ($0) plan with no seat billing or trial. */
741
+ isFreemium: boolean;
742
+ version: number;
743
+ status: 'draft' | 'published';
744
+ /** Multi-currency pricing. Use getBasePriceCents(plan, currency, interval) and getQuotaDisplayWithVariant for display. */
745
+ pricingVariants?: IPricingVariant[];
746
+ /** Keyed by item slug. Per-interval: included, unitSize; overage from pricingVariant.quotaOverages. */
747
+ quotas: Record<string, IQuotaByInterval>;
748
+ features: Record<string, boolean>;
749
+ limits: Record<string, number>;
750
+ /** Trial config. Only present when the plan has trials enabled. */
751
+ trial?: {
752
+ enabled: boolean;
753
+ durationDays: number;
754
+ requireCard: boolean;
755
+ };
756
+ /** Credit grant config. Only present when the plan grants credits. */
757
+ creditGrant?: {
758
+ enabled: boolean;
759
+ creditAmount: number | null;
760
+ packageName: string | null;
761
+ mode: 'reset' | 'topup';
762
+ /** true = credits given fresh every month, false = one-time lifetime grant. */
763
+ renewOnPeriod: boolean;
764
+ };
765
+ }
766
+ interface IPublicPlansResponse {
767
+ items: IPublicPlanItem[];
768
+ plans: IPublicPlanVersion[];
769
+ /** Optional note from API (e.g. "Pricing is in cents. Please convert to dollars for display."). */
770
+ notes?: string;
771
+ }
772
+ type InvoiceStatus = 'draft' | 'open' | 'paid' | 'uncollectible' | 'void';
773
+ interface IInvoice {
774
+ id: string;
775
+ amount_due: number;
776
+ number: string | null;
777
+ amount_paid: number;
778
+ currency: string;
779
+ status: InvoiceStatus;
780
+ created: number;
781
+ due_date: number | null;
782
+ hosted_invoice_url: string;
783
+ invoice_pdf: string | null;
784
+ description: string | null;
785
+ subscription: string;
786
+ }
787
+ interface IInvoiceListResponse {
788
+ invoices: IInvoice[];
789
+ has_more: boolean;
790
+ }
791
+ interface IInvoiceResponse {
792
+ invoice: IInvoice;
793
+ }
794
+ /** Request body for POST .../workspaces/:id/subscription/usage */
795
+ interface IRecordUsageRequest {
796
+ quotaSlug: string;
797
+ quantity: number;
798
+ metadata?: Record<string, unknown>;
799
+ source?: string;
800
+ idempotencyKey?: string;
801
+ }
802
+ /** Response from POST .../workspaces/:id/subscription/usage */
803
+ interface IRecordUsageResponse {
804
+ used: number;
805
+ consumed: number;
806
+ included: number;
807
+ available: number;
808
+ overage: number;
809
+ billedAsync: boolean;
810
+ }
811
+ /** Single quota usage status (shared between status and all endpoints). */
812
+ interface IQuotaUsageStatus {
813
+ consumed: number;
814
+ included: number;
815
+ available: number;
816
+ overage: number;
817
+ hasOverage: boolean;
818
+ /** Whether over-usage is allowed (billed as overage). When false, usage is hard-capped. Default true. */
819
+ allowOverage?: boolean;
820
+ }
821
+ /** Response from GET .../workspaces/:id/subscription/usage/status?quotaSlug=X */
822
+ interface IQuotaUsageStatusResponse extends IQuotaUsageStatus {
823
+ quotaSlug: string;
824
+ }
825
+ /** Response from GET .../workspaces/:id/subscription/usage/all */
826
+ interface IAllQuotaUsageResponse {
827
+ quotas: Record<string, IQuotaUsageStatus>;
828
+ }
829
+ /** Single usage log entry from GET .../usage/logs */
830
+ interface IUsageLogEntry {
831
+ _id: string;
832
+ quotaSlug: string;
833
+ quantity: number;
834
+ source?: string;
835
+ workspace: string;
836
+ createdAt: string;
837
+ updatedAt: string;
838
+ }
839
+ /** Query parameters for GET .../workspaces/:id/subscription/usage/logs */
840
+ interface IUsageLogsQuery {
841
+ quotaSlug?: string;
842
+ from?: string;
843
+ to?: string;
844
+ source?: string;
845
+ page?: number;
846
+ limit?: number;
847
+ }
848
+ /** Paginated response from GET .../workspaces/:id/subscription/usage/logs */
849
+ interface IUsageLogsResponse {
850
+ docs: IUsageLogEntry[];
851
+ totalDocs: number;
852
+ limit: number;
853
+ page: number;
854
+ totalPages: number;
855
+ hasNextPage: boolean;
856
+ hasPrevPage: boolean;
857
+ }
858
+ /** Credit bucket source (how credits entered the workspace) */
859
+ declare const CreditBucketSource: {
860
+ readonly PlanGrant: "plan_grant";
861
+ readonly CreditPackPurchase: "credit_pack_purchase";
862
+ readonly AdminGrant: "admin_grant";
863
+ readonly Refund: "refund";
864
+ };
865
+ type CreditBucketSourceType = (typeof CreditBucketSource)[keyof typeof CreditBucketSource];
866
+ /** Credit bucket status */
867
+ declare const CreditBucketStatus: {
868
+ readonly Pending: "pending";
869
+ readonly Active: "active";
870
+ readonly Expired: "expired";
871
+ readonly Consumed: "consumed";
872
+ };
873
+ type CreditBucketStatusType = (typeof CreditBucketStatus)[keyof typeof CreditBucketStatus];
874
+ /** Credit transaction type */
875
+ declare const CreditTransactionType: {
876
+ readonly PlanGrant: "plan_grant";
877
+ readonly PackPurchased: "pack_purchased";
878
+ readonly Consumed: "consumed";
879
+ readonly Expired: "expired";
880
+ readonly AdminGrant: "admin_grant";
881
+ readonly AdminRevoke: "admin_revoke";
882
+ readonly Refund: "refund";
883
+ };
884
+ type CreditTransactionTypeValue = (typeof CreditTransactionType)[keyof typeof CreditTransactionType];
885
+ /** Credit balance for a workspace */
886
+ interface ICreditBalance {
887
+ available: number;
888
+ totalGranted: number;
889
+ totalConsumed: number;
890
+ totalExpired: number;
891
+ totalRefunded: number;
892
+ }
893
+ /** Pricing variant for a credit package */
894
+ interface ICreditPackagePricingVariant {
895
+ currency: string;
896
+ /** Price in smallest currency unit (cents) */
897
+ amount: number;
898
+ stripePriceId?: string;
899
+ }
900
+ /** Admin-defined purchasable credit package */
901
+ interface ICreditPackage extends IDocument {
902
+ name: string;
903
+ slug: string;
904
+ description?: string;
905
+ creditAmount: number;
906
+ pricingVariants: ICreditPackagePricingVariant[];
907
+ validityDays?: number;
908
+ isActive: boolean;
909
+ sortOrder: number;
910
+ archived: boolean;
911
+ }
912
+ /** A single credit transaction (user-facing event history) */
913
+ interface ICreditTransaction extends IDocument {
914
+ workspace: string;
915
+ type: CreditTransactionTypeValue;
916
+ /** Positive for grants, negative for consume/expire/revoke */
917
+ amount: number;
918
+ balanceAfter: number;
919
+ description?: string;
920
+ bucket?: string;
921
+ creditPackage?: string;
922
+ actor?: string;
923
+ metadata?: Record<string, any>;
924
+ }
925
+ /** A credit bucket (per-grant allocation with expiry) */
926
+ interface ICreditBucket extends IDocument {
927
+ workspace: string;
928
+ source: CreditBucketSourceType;
929
+ status: CreditBucketStatusType;
930
+ creditAmount: number;
931
+ remaining: number;
932
+ expiresAt?: string;
933
+ activatedAt?: string;
934
+ creditPackage?: string | ICreditPackage;
935
+ grantedBy?: string;
936
+ metadata?: Record<string, any>;
937
+ }
938
+ /** Request body for POST .../credits/consume */
939
+ interface IConsumeCreditsRequest {
940
+ amount: number;
941
+ description?: string;
942
+ idempotencyKey?: string;
943
+ metadata?: Record<string, any>;
944
+ }
945
+ /** Response from POST .../credits/consume */
946
+ interface IConsumeCreditsResponse {
947
+ success: boolean;
948
+ consumed: number;
949
+ balanceAfter: number;
950
+ }
951
+ /** Request body for POST .../credits/purchase */
952
+ interface ICreditPurchaseRequest {
953
+ creditPackageId: string;
954
+ currency?: string;
955
+ successUrl: string;
956
+ cancelUrl: string;
957
+ }
958
+ /** Response from POST .../credits/purchase */
959
+ interface ICreditPurchaseResponse {
960
+ sessionId: string;
961
+ url: string;
962
+ }
963
+ /** Query params for GET .../credits/transactions */
964
+ interface ICreditTransactionsQuery {
965
+ type?: CreditTransactionTypeValue;
966
+ page?: number;
967
+ limit?: number;
968
+ }
969
+ /** Paginated response from GET .../credits/transactions */
970
+ interface ICreditTransactionsResponse {
971
+ docs: ICreditTransaction[];
972
+ totalDocs: number;
973
+ limit: number;
974
+ page: number;
975
+ totalPages: number;
976
+ hasNextPage: boolean;
977
+ hasPrevPage: boolean;
978
+ }
979
+ /** Query params for GET .../credits/buckets */
980
+ interface ICreditBucketsQuery {
981
+ status?: CreditBucketStatusType;
982
+ source?: CreditBucketSourceType;
983
+ page?: number;
984
+ limit?: number;
985
+ }
986
+ /** Paginated response from GET .../credits/buckets */
987
+ interface ICreditBucketsResponse {
988
+ docs: ICreditBucket[];
989
+ totalDocs: number;
990
+ limit: number;
991
+ page: number;
992
+ totalPages: number;
993
+ hasNextPage: boolean;
994
+ hasPrevPage: boolean;
995
+ }
996
+ /** Response from GET .../credits/expiring */
997
+ interface IExpiringCreditsResponse {
998
+ days: number;
999
+ expiringCredits: number;
1000
+ buckets: ICreditBucket[];
1001
+ }
1002
+ /** Public credit package (from public endpoint, no auth) */
1003
+ interface IPublicCreditPackage {
1004
+ _id: string;
1005
+ name: string;
1006
+ slug: string;
1007
+ description: string;
1008
+ creditAmount: number;
1009
+ pricingVariants: ICreditPackagePricingVariant[];
1010
+ validityDays: number | null;
1011
+ sortOrder: number;
1012
+ }
1013
+ /** Response from GET /:orgId/credit-packages (public, no auth) */
1014
+ interface IPublicCreditPackagesResponse {
1015
+ packages: IPublicCreditPackage[];
1016
+ notes?: string;
1017
+ }
1018
+
1019
+ interface IWorkspace {
1020
+ _id: string;
1021
+ name: string;
1022
+ image?: string;
1023
+ workspaceId: string;
1024
+ users: IUser[];
1025
+ roles: string[];
1026
+ createdBy: string | IUser;
1027
+ features: Record<string, boolean>;
1028
+ /**
1029
+ * Quota usage tracking: { [quotaSlug]: number } – how much of each quota has been used.
1030
+ */
1031
+ quotas?: Record<string, number>;
1032
+ /**
1033
+ * Subscription limits snapshot: { [limitSlug]: number | null } – synced from subscription plan.
1034
+ * Limits are maximum values (e.g. max-users, max-workspaces). Updated when subscription is assigned/updated.
1035
+ */
1036
+ limits?: Record<string, number | null>;
1037
+ subscription?: ISubscription | string | null;
1038
+ /** Stripe Customer ID for this workspace. */
1039
+ stripeCustomerId?: string;
1040
+ /**
1041
+ * Billing currency locked for this workspace (set on first subscription).
1042
+ * Stripe allows one currency per customer; all future subscriptions must use this currency.
1043
+ * When set, subscription UI only shows/uses this currency.
1044
+ */
1045
+ billingCurrency?: string | null;
1046
+ /** Per-workspace permission overrides. Takes priority over org-level settings.permissions. */
1047
+ permissions?: Record<string, string[]>;
1048
+ /** Set when workspace first uses a trial. Blocks trial from being used again on the same workspace. */
1049
+ trialUsedAt?: string | null;
1050
+ }
1051
+ interface IWorkspaceFeature {
1052
+ _id: string;
1053
+ name: string;
1054
+ description: string;
1055
+ userManaged: boolean;
1056
+ defaultValue: boolean;
1057
+ slug: string;
1058
+ category: string;
1059
+ createdAt: Date;
1060
+ updatedAt: Date;
1061
+ }
1062
+ interface IWorkspaceUser {
1063
+ _id: string;
1064
+ workspace: string | IWorkspace;
1065
+ user: string | IUser;
1066
+ role: string;
1067
+ }
1068
+
1069
+ type WorkspaceMode = 'personal' | 'platform';
1070
+ interface ISettings {
1071
+ workspace: {
1072
+ roles: string[];
1073
+ defaultRole: string;
1074
+ maxWorkspaces: number;
1075
+ maxWorkspaceUsers: number;
1076
+ /** Workspace mode: 'personal' (solo) or 'platform' (multi-user, default) */
1077
+ mode: WorkspaceMode;
1078
+ /** Whether users can create workspaces. false = disabled, 'owner-only' = restricted */
1079
+ canCreateWorkspace: boolean | 'owner-only';
1080
+ /** Whether members can invite others. false = disabled, 'admin-only' = restricted */
1081
+ canInviteMembers: boolean | 'admin-only';
1082
+ /** Custom role→permission mapping. Overrides SDK defaults when provided by backend. */
1083
+ permissions?: Record<string, string[]>;
1084
+ /** Show workspace switcher in the UI */
1085
+ showSwitcher: boolean;
1086
+ /** Max workspaces a user can own. 0 = unlimited */
1087
+ maxWorkspacesPerUser: number;
1088
+ /** Auto-create workspace on first login */
1089
+ autoCreateFirst: boolean;
1090
+ };
1091
+ [key: string]: any;
1092
+ }
1093
+
1094
+ /** Data passed to a notification template for merge tag replacement. */
1095
+ interface NotificationData {
1096
+ /** Push notification title. Falls back to the event name if not provided. */
1097
+ title?: string;
1098
+ /** Primary message — used in email body and as push notification body. */
1099
+ message?: string;
1100
+ /** Custom icon URL for the push notification. Falls back to the org icon. */
1101
+ icon?: string;
1102
+ /** Large image URL displayed in the push notification body. */
1103
+ image?: string;
1104
+ /** Small monochrome icon for the status bar (Android/ChromeOS). */
1105
+ badge?: string;
1106
+ /** URL to open when the user clicks the push notification. Also available as {{url}} in email template. */
1107
+ url?: string;
1108
+ /** Tag to group/replace notifications. New notification with same tag replaces the previous one instead of stacking. */
1109
+ tag?: string;
1110
+ /** Action buttons displayed on the notification (max 2). */
1111
+ actions?: Array<{
1112
+ action: string;
1113
+ title: string;
1114
+ icon?: string;
1115
+ }>;
1116
+ /** Show notification without sound or vibration. */
1117
+ silent?: boolean;
1118
+ /** Keep notification visible until user interacts (no auto-dismiss). Useful for critical alerts. */
1119
+ requireInteraction?: boolean;
1120
+ /** Vibrate/sound again when replacing a notification with the same tag. Only works with `tag`. */
1121
+ renotify?: boolean;
1122
+ /** Custom timestamp (ms since epoch) displayed on the notification. */
1123
+ timestamp?: number;
1124
+ /** Text direction for title/body. */
1125
+ dir?: 'ltr' | 'rtl' | 'auto';
1126
+ /** Time-to-live in seconds. Push service discards the message if not delivered within this time. Default: 86400 (24h). */
1127
+ ttl?: number;
1128
+ /** Delivery urgency hint. Affects battery/delivery priority on mobile. */
1129
+ urgency?: 'very-low' | 'low' | 'normal' | 'high';
1130
+ /** ISO 8601 date string. Delays delivery until the specified time. */
1131
+ scheduledAt?: string;
1132
+ /**
1133
+ * Which channels to send on. Overrides the event's default channel config.
1134
+ * Omit to use the event's configured channels.
1135
+ *
1136
+ * @example
1137
+ * ```ts
1138
+ * // Only push, no email
1139
+ * { channels: { push: true } }
1140
+ *
1141
+ * // Only email, no push
1142
+ * { channels: { email: true } }
1143
+ * ```
1144
+ */
1145
+ channels?: {
1146
+ email?: boolean;
1147
+ push?: boolean;
1148
+ };
1149
+ /** Any additional merge tags available as {{key}} in the email template. */
1150
+ [key: string]: any;
1151
+ }
1152
+ /** Result of sending a notification. */
1153
+ interface NotificationResult {
1154
+ sent: boolean;
1155
+ /** Which channels were actually used. false = blocked by settings or missing template. */
1156
+ channels: {
1157
+ email: boolean;
1158
+ push: boolean;
1159
+ };
1160
+ /** Number of users notified. 1 for single user, N for whole workspace. */
1161
+ notifiedCount?: number;
1162
+ /** Reason if not sent (e.g., 'event_disabled'). Only present when sent=false. */
1163
+ reason?: string;
1164
+ }
1165
+
1166
+ interface WorkspaceActions {
1167
+ list(): Promise<IWorkspace[]>;
1168
+ get(workspaceId: string): Promise<IWorkspace>;
1169
+ create(data: {
1170
+ name: string;
1171
+ image?: string;
1172
+ }): Promise<IWorkspace>;
1173
+ update(workspaceId: string, data: Partial<IWorkspace>): Promise<IWorkspace>;
1174
+ delete(workspaceId: string): Promise<{
1175
+ success: boolean;
1176
+ }>;
1177
+ }
1178
+ interface UserActions {
1179
+ list(workspaceId: string): Promise<IWorkspaceUser[]>;
1180
+ invite(workspaceId: string, email: string, role: string): Promise<{
1181
+ userId: string;
1182
+ workspace: IWorkspace;
1183
+ message: string;
1184
+ }>;
1185
+ remove(workspaceId: string, userId: string): Promise<{
1186
+ userId: string;
1187
+ workspace: IWorkspace;
1188
+ message: string;
1189
+ }>;
1190
+ updateRole(workspaceId: string, userId: string, role: string): Promise<{
1191
+ userId: string;
1192
+ workspace: IWorkspace;
1193
+ message: string;
1194
+ }>;
1195
+ getProfile(): Promise<IUser>;
1196
+ updateProfile(data: Partial<IUser>): Promise<IUser>;
1197
+ }
1198
+ interface SubscriptionActions {
1199
+ get(workspaceId: string): Promise<ISubscriptionResponse>;
1200
+ checkout(workspaceId: string, request: ICheckoutSessionRequest): Promise<CheckoutResult | ICheckoutSessionResponse>;
1201
+ update(workspaceId: string, request: ISubscriptionUpdateRequest): Promise<ISubscriptionUpdateResponse | ICheckoutSessionResponse>;
1202
+ cancel(workspaceId: string): Promise<ISubscriptionResponse>;
1203
+ resume(workspaceId: string): Promise<ISubscriptionResponse>;
1204
+ getBillingPortalUrl(workspaceId: string, returnUrl?: string): Promise<{
1205
+ url: string;
1206
+ }>;
1207
+ }
1208
+ interface PlanActions {
1209
+ getGroup(workspaceId: string): Promise<IPlanGroupResponse>;
1210
+ getVersions(workspaceId: string): Promise<IPlanGroupVersionsResponse>;
1211
+ /** No auth required */
1212
+ getPublic(slug: string): Promise<IPublicPlansResponse>;
1213
+ /** No auth required */
1214
+ getVersion(groupVersionId: string): Promise<IPlanGroupVersion>;
1215
+ }
1216
+ interface InvoiceActions {
1217
+ list(workspaceId: string, limit?: number, startingAfter?: string): Promise<IInvoiceListResponse>;
1218
+ get(workspaceId: string, invoiceId: string): Promise<IInvoiceResponse>;
1219
+ }
1220
+ interface UsageBatchRequest {
1221
+ items: Array<{
1222
+ quotaSlug: string;
1223
+ quantity: number;
1224
+ metadata?: Record<string, any>;
1225
+ source?: string;
1226
+ idempotencyKey?: string;
1227
+ }>;
1228
+ }
1229
+ interface UsageBatchResponse {
1230
+ success: boolean;
1231
+ total: number;
1232
+ succeeded: number;
1233
+ failed: number;
1234
+ results: Array<{
1235
+ success: boolean;
1236
+ quotaSlug: string;
1237
+ quantity: number;
1238
+ error?: string;
1239
+ }>;
1240
+ }
1241
+ interface UsageActions {
1242
+ record(workspaceId: string, request: IRecordUsageRequest): Promise<IRecordUsageResponse>;
1243
+ /** Record multiple usage entries in one request. Max 100 items. For bulk/batch operations. */
1244
+ recordBatch(workspaceId: string, request: UsageBatchRequest): Promise<UsageBatchResponse>;
1245
+ getQuota(workspaceId: string, quotaSlug: string): Promise<IQuotaUsageStatusResponse>;
1246
+ getAll(workspaceId: string): Promise<IAllQuotaUsageResponse>;
1247
+ getLogs(workspaceId: string, query?: IUsageLogsQuery): Promise<IUsageLogsResponse>;
1248
+ }
1249
+ interface SettingsActions {
1250
+ get(): Promise<ISettings>;
1251
+ }
1252
+ interface FeatureActions {
1253
+ list(): Promise<IWorkspaceFeature[]>;
1254
+ update(workspaceId: string, key: string, value: boolean): Promise<IWorkspace>;
1255
+ }
1256
+ interface PermissionActions {
1257
+ /** Check if a user has a permission (or all of an array) in a workspace. Works with both platform permissions (Permission.*) and app permissions (custom strings). */
1258
+ check(workspaceId: string, userId: string, permission: string | string[]): Promise<boolean>;
1259
+ /** Resolve all permissions for a user in a workspace. Returns both platform and app permissions. */
1260
+ resolve(workspaceId: string, userId: string): Promise<Set<string>>;
1261
+ }
1262
+ interface NotificationActions {
1263
+ /**
1264
+ * Send a notification to a user or all workspace members.
1265
+ * Respects all notification gates (org settings, event config, workspace preferences).
1266
+ *
1267
+ * @param workspaceId - The workspace context
1268
+ * @param event - The event slug (e.g., 'comment_added')
1269
+ * @param userId - The user to notify. Omit to notify all workspace members.
1270
+ * @param data - Template merge data. `message` is used as push body.
1271
+ *
1272
+ * @example
1273
+ * ```ts
1274
+ * // Notify one user
1275
+ * await notification.send(workspaceId, 'comment_added', userId, {
1276
+ * message: 'Alice commented on your project',
1277
+ * })
1278
+ *
1279
+ * // Notify all workspace members
1280
+ * await notification.send(workspaceId, 'new_release', undefined, {
1281
+ * message: 'Version 2.0 is now available!',
1282
+ * })
1283
+ * ```
1284
+ */
1285
+ send(workspaceId: string, event: string, userId?: string, data?: NotificationData): Promise<NotificationResult>;
1286
+ }
1287
+ interface CreditActions {
1288
+ /** Get workspace credit balance */
1289
+ getBalance(workspaceId: string): Promise<ICreditBalance>;
1290
+ /** Consume credits from workspace. Throws with code 'INSUFFICIENT_CREDITS' on 402. */
1291
+ consume(workspaceId: string, request: IConsumeCreditsRequest): Promise<IConsumeCreditsResponse>;
1292
+ /** Create Stripe checkout session for purchasing a credit package */
1293
+ purchase(workspaceId: string, request: ICreditPurchaseRequest): Promise<ICreditPurchaseResponse>;
1294
+ /** List available credit packages for purchase */
1295
+ getPackages(workspaceId: string): Promise<ICreditPackage[]>;
1296
+ /** Get paginated credit transaction history */
1297
+ getTransactions(workspaceId: string, query?: ICreditTransactionsQuery): Promise<ICreditTransactionsResponse>;
1298
+ /** Get paginated credit buckets */
1299
+ getBuckets(workspaceId: string, query?: ICreditBucketsQuery): Promise<ICreditBucketsResponse>;
1300
+ /** Get credits expiring within N days (default 7) */
1301
+ getExpiring(workspaceId: string, days?: number): Promise<IExpiringCreditsResponse>;
1302
+ /** Get credit packages publicly (no auth required) */
1303
+ getPublicPackages(): Promise<IPublicCreditPackagesResponse>;
1304
+ }
1305
+ /** All action modules bound to a specific session. Returned by `withSession()`. */
1306
+ interface ScopedActions {
1307
+ workspace: WorkspaceActions;
1308
+ users: UserActions;
1309
+ subscription: SubscriptionActions;
1310
+ plans: PlanActions;
1311
+ invoices: InvoiceActions;
1312
+ usage: UsageActions;
1313
+ settings: SettingsActions;
1314
+ features: FeatureActions;
1315
+ permissions: PermissionActions;
1316
+ notification: NotificationActions;
1317
+ credits: CreditActions;
1318
+ }
1319
+
1320
+ /**
1321
+ * MCP server toolkit (server-side, framework-agnostic).
1322
+ *
1323
+ * Turns a consuming app into a live MCP (Model Context Protocol) server so AI
1324
+ * agents can operate its BuildBase capabilities — workspaces, subscriptions,
1325
+ * quotas, credits, feature flags, permissions — plus any custom tools the app
1326
+ * defines. The transport is stateless Streamable HTTP: a single JSON-RPC
1327
+ * message per POST, no sessions and no SSE, which keeps the handler pure and
1328
+ * identical on Node 18+, edge, Deno, and Bun.
1329
+ *
1330
+ * Zero framework types: the core `handle()` takes a plain
1331
+ * `{ method, headers, body }` and returns `{ status, headers, body }` (wire it
1332
+ * into Express/Fastify/Hono yourself), while `fetch()` adapts Web-standard
1333
+ * `Request` → `Response` for Next.js App Router and edge runtimes.
1334
+ *
1335
+ * Agents authenticate with the Bearer tokens the app mints via the OAuth2
1336
+ * app-bridge (`handleAppTokenRequest` + `signClientJwt`); the `auth.verify`
1337
+ * callback maps a verified token to the BuildBase session the tools run under.
1338
+ *
1339
+ * @example Next.js App Router — the entire integration:
1340
+ * ```ts
1341
+ * // lib/mcp.ts
1342
+ * import BuildBase from '@buildbase/sdk';
1343
+ * import { createMcpHandler, verifyClientJwt } from '@buildbase/sdk/mcp';
1344
+ *
1345
+ * const buildbase = BuildBase({
1346
+ * serverUrl: process.env.BUILDBASE_URL!,
1347
+ * orgId: process.env.BUILDBASE_ORG_ID!,
1348
+ * });
1349
+ *
1350
+ * export const mcp = createMcpHandler({
1351
+ * buildbase,
1352
+ * serverInfo: { name: 'imejis', version: '1.0.0' },
1353
+ * auth: {
1354
+ * verify: (token) => {
1355
+ * const c = verifyClientJwt(token, process.env.BUILDBASE_CLIENT_SECRET!);
1356
+ * return { sessionId: String(c.sid), userId: String(c.sub), scopes: c.scope };
1357
+ * },
1358
+ * resourceMetadataUrl: 'https://imejis.io/.well-known/oauth-protected-resource',
1359
+ * },
1360
+ * });
1361
+ *
1362
+ * // app/api/mcp/route.ts
1363
+ * import { mcp } from '@/lib/mcp';
1364
+ * export const POST = (req: Request) => mcp.fetch(req);
1365
+ * export const GET = (req: Request) => mcp.fetch(req); // 405 per spec (no SSE)
1366
+ * export const DELETE = (req: Request) => mcp.fetch(req); // 405 (stateless)
1367
+ * ```
1368
+ */
1369
+
1370
+ /**
1371
+ * The slice of the `BuildBase()` client the MCP handler needs. Structural on
1372
+ * purpose (instead of the full `BuildBaseResult`) so the type stays assignable
1373
+ * across the SDK's separately-bundled entry points.
1374
+ */
1375
+ interface McpBuildBaseClient {
1376
+ withSession(sessionId: string): ScopedActions;
1377
+ }
1378
+ /** A transport-agnostic HTTP request (mirror of the DiscoveryDocument idea). */
1379
+ interface McpHttpRequest {
1380
+ /** HTTP method, e.g. `'POST'`. */
1381
+ method: string;
1382
+ /** Header map. Keys are matched case-insensitively. */
1383
+ headers: Record<string, string | undefined>;
1384
+ /** Raw JSON body string, or an already-parsed value (e.g. Express req.body). */
1385
+ body?: string | unknown;
1386
+ }
1387
+ /** A rendered HTTP response ready to hand to any framework. */
1388
+ interface McpHttpResponse {
1389
+ status: number;
1390
+ headers: Record<string, string>;
1391
+ body: string;
1392
+ }
1393
+ /** The identity a verified Bearer token resolves to. */
1394
+ interface McpAuthInfo {
1395
+ /** BuildBase session all tool calls run under (`buildbase.withSession(...)`). */
1396
+ sessionId: string;
1397
+ /** The acting BuildBase user id — default subject for permission checks. */
1398
+ userId?: string;
1399
+ /** Pin tools to one workspace; used when the agent omits `workspaceId`. */
1400
+ workspaceId?: string;
1401
+ /** OAuth scopes granted to the agent; filters tools with `requiredScopes`. */
1402
+ scopes?: string[];
1403
+ /** Raw verified claims, passed through to tool context. */
1404
+ claims?: Record<string, unknown>;
1405
+ }
1406
+ /** Context passed to every tool execution. */
1407
+ interface McpToolContext<TCustom = unknown> {
1408
+ /**
1409
+ * Session-scoped BuildBase actions (`workspace`, `usage`, `credits`, …).
1410
+ * Only usable when a `buildbase` client was configured — otherwise any
1411
+ * access throws a clear error (tools that don't touch BuildBase never
1412
+ * notice).
1413
+ */
1414
+ bb: ScopedActions;
1415
+ auth: McpAuthInfo;
1416
+ /** Default workspace (from `auth.workspaceId`), if any. */
1417
+ workspaceId?: string;
1418
+ /**
1419
+ * Whatever your `context` factory returned for this request — your own
1420
+ * database handles, services, request info. `undefined` when no factory is
1421
+ * configured.
1422
+ */
1423
+ custom: TCustom;
1424
+ }
1425
+ /** MCP tool annotations (behavior hints for agents). */
1426
+ interface McpToolAnnotations {
1427
+ title?: string;
1428
+ readOnlyHint?: boolean;
1429
+ destructiveHint?: boolean;
1430
+ idempotentHint?: boolean;
1431
+ openWorldHint?: boolean;
1432
+ }
1433
+ /** A tool exposed over MCP. Use {@link defineMcpTool} for inferred input types. */
1434
+ interface McpToolDefinition<S extends z.ZodType = z.ZodType, TCustom = any> {
1435
+ /** Tool name: `[a-zA-Z0-9_-]`, 1–128 chars. */
1436
+ name: string;
1437
+ description: string;
1438
+ /**
1439
+ * Input schema: a zod schema (validated, converted to JSON Schema for
1440
+ * `tools/list`) or a raw JSON Schema object (served as-is, not validated).
1441
+ */
1442
+ inputSchema: S | Record<string, unknown>;
1443
+ annotations?: McpToolAnnotations;
1444
+ /**
1445
+ * Scopes required to see/call this tool. The token must carry ALL of them
1446
+ * (`McpAuthInfo.scopes`) for the tool to be visible or callable; a token
1447
+ * with no scopes sees only tools that declare none. Built-in tools declare
1448
+ * no scopes and are governed by `builtinTools` + the user's own permissions.
1449
+ */
1450
+ requiredScopes?: string[];
1451
+ execute: (input: z.output<S>, ctx: McpToolContext<TCustom>) => unknown | Promise<unknown>;
1452
+ }
1453
+ /** Configuration for {@link createMcpHandler}. */
1454
+ interface CreateMcpHandlerConfig {
1455
+ /**
1456
+ * The `BuildBase()` instance the built-in tools run against (via
1457
+ * `withSession`). Optional — omit it to run a standalone MCP server with
1458
+ * only your own tools (built-ins are then disabled and `ctx.bb` throws a
1459
+ * clear error if a tool touches it).
1460
+ */
1461
+ buildbase?: McpBuildBaseClient;
1462
+ /** Advertised in `initialize`. Defaults to `buildbase-mcp`. */
1463
+ serverInfo?: {
1464
+ name: string;
1465
+ version: string;
1466
+ };
1467
+ /** Optional usage instructions surfaced to agents on `initialize`. */
1468
+ instructions?: string;
1469
+ /**
1470
+ * Per-request context factory for YOUR tools: whatever it returns (your
1471
+ * database, services, request info) is available as `ctx.custom` in every
1472
+ * `execute`. Runs once per `tools/call`, after auth.
1473
+ */
1474
+ context?: (auth: McpAuthInfo | null, req: McpHttpRequest) => unknown | Promise<unknown>;
1475
+ /**
1476
+ * Bearer-token verification. Return null to reject (401). Set `false` to
1477
+ * disable auth entirely — local development only.
1478
+ */
1479
+ auth: {
1480
+ verify: (token: string, req: McpHttpRequest) => McpAuthInfo | null | Promise<McpAuthInfo | null>;
1481
+ /** When set, 401s carry the RFC 9728 `WWW-Authenticate` challenge. */
1482
+ resourceMetadataUrl?: string;
1483
+ } | false;
1484
+ /**
1485
+ * App-defined tools. A custom tool with a built-in's name REPLACES the
1486
+ * built-in (your definition wins); duplicate custom names or invalid names
1487
+ * throw at create time.
1488
+ */
1489
+ tools?: McpToolDefinition<any, any>[];
1490
+ /**
1491
+ * Which built-in BuildBase tools to expose. **Defaults to `'readonly'`**
1492
+ * when a `buildbase` client is configured (`false` otherwise) — reads only,
1493
+ * so no agent can mutate, bill, or delete anything unless you opt in. Set
1494
+ * `'all'` to expose the full surface (reads + writes + destructive/billing
1495
+ * ops like workspace delete, subscription cancel, credit purchase); `false`
1496
+ * for none; or an `{ include, exclude }` selection to hand-pick. Every tool
1497
+ * still runs under the user's session, so the platform enforces the user's
1498
+ * real permissions on top — but least privilege starts here.
1499
+ */
1500
+ builtinTools?: 'all' | 'readonly' | false | {
1501
+ include?: BuiltinMcpToolName[];
1502
+ exclude?: BuiltinMcpToolName[];
1503
+ };
1504
+ /**
1505
+ * Allowed `Origin` header values (DNS-rebinding protection). When set, a
1506
+ * request with a non-matching Origin is rejected with 403. Requests without
1507
+ * an Origin header (curl, server-to-server) always pass.
1508
+ */
1509
+ allowedOrigins?: string[];
1510
+ /**
1511
+ * CORS for browser-based MCP clients. Default **true**: emit
1512
+ * `Access-Control-Allow-Origin: *` (safe for a Bearer-token API — no
1513
+ * cookies are ever involved), or reflect the matching origin when
1514
+ * `allowedOrigins` is set. Pass an origin array to allow only those, or
1515
+ * `false` to emit no CORS headers at all. Preflight (OPTIONS) is answered
1516
+ * automatically either way — no wrapper needed in your route.
1517
+ */
1518
+ cors?: boolean | string[];
1519
+ /**
1520
+ * Hard cap on the raw request-body size in bytes (DoS protection). Bodies
1521
+ * larger than this are rejected with 413 before parsing. Defaults to
1522
+ * 1 MiB (`1_048_576`); set `0` to disable the check.
1523
+ */
1524
+ maxRequestBytes?: number;
1525
+ /**
1526
+ * Optional rate-limit gate, called after authentication and before the
1527
+ * JSON-RPC message is dispatched. Return `false` (or a `{ retryAfter }`
1528
+ * object) to reject the request with 429. The SDK is stateless and holds no
1529
+ * counters itself — back this with your own store (KV, Redis, edge limiter),
1530
+ * keyed by `auth.userId`/`auth.sessionId`. Recommended in production; also
1531
+ * apply platform/edge rate limiting in front of the endpoint.
1532
+ */
1533
+ rateLimit?: (auth: McpAuthInfo | null, req: McpHttpRequest) => boolean | {
1534
+ ok: boolean;
1535
+ retryAfter?: number;
1536
+ } | Promise<boolean | {
1537
+ ok: boolean;
1538
+ retryAfter?: number;
1539
+ }>;
1540
+ /** Called on tool-execution and auth-callback errors (for logging). */
1541
+ onError?: (error: Error, ctx: {
1542
+ method: string;
1543
+ tool?: string;
1544
+ }) => void;
1545
+ /**
1546
+ * Map a thrown tool error to the message returned to the agent. Use it to
1547
+ * redact internal details in production (the full error still reaches
1548
+ * `onError`). Defaults to the raw `error.message`. Return a generic string
1549
+ * — e.g. `() => 'Tool execution failed'` — to leak nothing.
1550
+ */
1551
+ formatToolError?: (error: Error, ctx: {
1552
+ tool: string;
1553
+ }) => string;
1554
+ }
1555
+ /** The handler returned by {@link createMcpHandler}. */
1556
+ interface McpHandler {
1557
+ /** Pure, framework-agnostic core (Express, Fastify, Hono, tests). */
1558
+ handle(req: McpHttpRequest): Promise<McpHttpResponse>;
1559
+ /** Web-standard adapter (Next.js App Router route handlers, edge). */
1560
+ fetch(request: Request): Promise<Response>;
1561
+ /** Metadata for the exposed tools (before per-request scope filtering). */
1562
+ listTools(): Array<{
1563
+ name: string;
1564
+ description: string;
1565
+ annotations?: McpToolAnnotations;
1566
+ }>;
1567
+ /** A ready-to-assign `AgentReadyConfig.mcpServerCard` for this server. */
1568
+ serverCard(options: {
1569
+ endpoint: string;
1570
+ description?: string;
1571
+ documentationUrl?: string;
1572
+ }): McpServerCard;
1573
+ }
1574
+ /**
1575
+ * Create a stateless MCP server handler exposing BuildBase capabilities (and
1576
+ * any custom tools) over Streamable HTTP. See the module docs for wiring.
1577
+ */
1578
+ declare function createMcpHandler(config: CreateMcpHandlerConfig): McpHandler;
1579
+ /**
1580
+ * Identity helper that infers the tool's input type from its zod schema.
1581
+ * Pass a second type argument to type `ctx.custom` from your `context`
1582
+ * factory: `defineMcpTool<typeof schema, MyContext>({ ... })`.
1583
+ */
1584
+ declare function defineMcpTool<S extends z.ZodType, TCustom = any>(tool: McpToolDefinition<S, TCustom>): McpToolDefinition<S, TCustom>;
1585
+
1586
+ /**
1587
+ * OAuth2 app-bridge toolkit (server-side).
1588
+ *
1589
+ * BuildBase runs the OAuth2 authorization flow (login, consent, code, PKCE,
1590
+ * refresh rotation) but it never mints the access token itself — on the token
1591
+ * grant it calls the org's own backend to mint the token the agent will carry.
1592
+ * Concretely, the platform makes signed webhook-style calls to two endpoints an
1593
+ * OAuth2 client registers:
1594
+ *
1595
+ * • applicationTokenUrl — mint an access token for a user (required)
1596
+ * • applicationRevokeUrl — invalidate a user's token on revocation (optional)
1597
+ *
1598
+ * Both requests carry an `Authorization: Bearer <JWT>` where the JWT is HS256,
1599
+ * signed with the client's secret (so possession of the secret authenticates
1600
+ * the platform). This module verifies those requests and shapes the responses,
1601
+ * so an implementor writes only the part that is theirs: minting/invalidating a
1602
+ * token in their own format.
1603
+ *
1604
+ * HMAC verification uses the SDK's dependency-free pure-JS HMAC-SHA256 (no Web
1605
+ * Crypto, no Node `crypto`, no `require`), so it behaves identically whether the
1606
+ * bundle is loaded as ESM or CJS, under a bundler or on bare Node.
1607
+ *
1608
+ * @example applicationTokenUrl handler (Next.js Pages Router):
1609
+ * ```ts
1610
+ * import { handleAppTokenRequest } from "@buildbase/sdk";
1611
+ * export default async function handler(req, res) {
1612
+ * const { status, body } = await handleAppTokenRequest({
1613
+ * authorization: req.headers.authorization,
1614
+ * clientSecret: process.env.BUILDBASE_CLIENT_SECRET!,
1615
+ * mintToken: (user) => ({
1616
+ * token: signMyAccessToken(user, { aud: user.resource }), // your token, your format
1617
+ * expiresIn: 3600,
1618
+ * }),
1619
+ * });
1620
+ * res.status(status).json(body);
1621
+ * }
1622
+ * ```
1623
+ */
1624
+ /** Thrown when an incoming platform request fails verification. */
1625
+ declare class AppBridgeError extends Error {
1626
+ /** Machine-readable reason, e.g. `invalid_signature`, `token_expired`. */
1627
+ readonly code: string;
1628
+ constructor(code: string, message: string);
1629
+ }
1630
+ /**
1631
+ * The user + grant context BuildBase sends to `applicationTokenUrl`. The app
1632
+ * mints a token for this user; `resource` is the RFC 8707 audience(s) the token
1633
+ * should be bound to (stamp them as `aud`), and `scope` is what the user granted.
1634
+ * Unknown claims are preserved for forward-compatibility.
1635
+ */
1636
+ interface AppTokenRequestClaims {
1637
+ /** The user's id in the org. */
1638
+ id: string;
1639
+ email: string;
1640
+ name?: string;
1641
+ role?: string;
1642
+ emailVerified?: boolean;
1643
+ image?: string;
1644
+ blocked?: boolean;
1645
+ blockedReason?: string;
1646
+ blockedAt?: string;
1647
+ /** Which grant triggered this mint. */
1648
+ grantType?: 'authorization_code' | 'refresh_token';
1649
+ /** Scopes granted at authorization time. */
1650
+ scope?: string[];
1651
+ /** RFC 8707 resource indicator(s) — bind the minted token's `aud` to these. */
1652
+ resource?: string[];
1653
+ /** The OAuth2 client's display title. */
1654
+ clientTitle?: string;
1655
+ /**
1656
+ * Per-user BuildBase session bound to this grant. Embed it ENCRYPTED in the
1657
+ * token you mint (JWTs are signed, not encrypted) and never store it — your
1658
+ * MCP/agent endpoints decrypt it per request to call BuildBase as this user
1659
+ * (`buildbase.withSession(...)`). Fresh on every grant, including refresh.
1660
+ */
1661
+ sessionId?: string;
1662
+ [claim: string]: unknown;
1663
+ }
1664
+ /** Options for {@link verifyClientJwt} (all optional; secure defaults). */
1665
+ interface VerifyClientJwtOptions {
1666
+ /** Allowed clock skew in seconds for `exp`/`nbf`. Default 60. */
1667
+ clockToleranceSec?: number;
1668
+ /**
1669
+ * Require a numeric `exp` claim. Default **true** — a JWT with no expiry
1670
+ * would never expire, so a leaked token stays valid forever. Set `false`
1671
+ * only for a token type you know is intentionally non-expiring.
1672
+ */
1673
+ requireExp?: boolean;
1674
+ /** If set, `iss` must equal this value (RFC 7519 issuer check). */
1675
+ issuer?: string;
1676
+ /** If set, `aud` must equal (or, for an array `aud`, include) this value. */
1677
+ audience?: string;
1678
+ }
1679
+ /**
1680
+ * Verify a BuildBase-issued HS256 JWT (signed with the client secret) and return
1681
+ * its payload. Checks the signature (timing-safe), the algorithm, `exp`/`nbf`,
1682
+ * and — by default — that an `exp` is present at all. Optionally pins `iss`/`aud`.
1683
+ * Throws {@link AppBridgeError} on any failure.
1684
+ */
1685
+ declare function verifyClientJwt(token: string, clientSecret: string, options?: VerifyClientJwtOptions): Record<string, any>;
1686
+ /** Extract the token from an `Authorization: Bearer <token>` header. */
1687
+ declare function extractBearerToken(authorization: string | null | undefined): string | null;
1688
+ /**
1689
+ * Sign an HS256 JWT — the symmetric counterpart of {@link verifyClientJwt}.
1690
+ * Sets `iat` and `exp` (from `expiresInSec`, default 3600) unless the payload
1691
+ * already carries them. The intended use is minting the app's own agent access
1692
+ * tokens in an `applicationTokenUrl` handler, embedding whatever claims the
1693
+ * MCP/API layer needs to verify later (e.g. `sub`, `scope`, a session id).
1694
+ */
1695
+ declare function signClientJwt(payload: Record<string, unknown>, secret: string, options?: {
1696
+ expiresInSec?: number;
1697
+ }): string;
1698
+ declare function bearerChallenge(options: {
1699
+ resourceMetadataUrl: string;
1700
+ error?: 'invalid_token' | 'insufficient_scope' | string;
1701
+ errorDescription?: string;
1702
+ }): {
1703
+ status: number;
1704
+ headers: Record<string, string>;
1705
+ body: string;
1706
+ };
1707
+
1708
+ /**
1709
+ * createAgentStack — the whole agent-readiness + MCP surface from ONE config.
1710
+ *
1711
+ * Everything the SDK can derive, it derives: the MCP endpoint, the server
1712
+ * card, the RFC 9728 protected-resource metadata, the API-catalog entry, the
1713
+ * BuildBase client, token verification (via `buildbaseAuth`), CORS, and the
1714
+ * discovery `Link` header — so a consumer configures identity once and wires
1715
+ * two routes. Every derived value stays overridable ("accelerate, never
1716
+ * gate").
1717
+ *
1718
+ * @example Next.js App Router — complete agent support:
1719
+ * ```ts
1720
+ * // lib/agent.ts
1721
+ * import { createAgentStack } from "@buildbase/sdk/mcp";
1722
+ * export const agent = createAgentStack({
1723
+ * serverUrl: process.env.NEXT_PUBLIC_BUILDBASE_SERVER_URL!,
1724
+ * orgId: process.env.NEXT_PUBLIC_BUILDBASE_ORG_ID!,
1725
+ * siteUrl: "https://app.example.com",
1726
+ * site: { name: "My App" },
1727
+ * secret: process.env.SYSTEM_SECRET!,
1728
+ * });
1729
+ *
1730
+ * // app/api/mcp/route.ts
1731
+ * import { agent } from "@/lib/agent";
1732
+ * export const { GET, POST, DELETE, OPTIONS } = agent.routes;
1733
+ *
1734
+ * // app/.well-known/[...path]/route.ts — and identical one-liners at
1735
+ * // app/robots.txt/route.ts, app/auth.md/route.ts, app/security.txt/route.ts
1736
+ * import { agent } from "@/lib/agent";
1737
+ * export const GET = agent.serveAgentPath;
1738
+ * ```
1739
+ *
1740
+ * @example Express / Hono / Bun — same stack, `handle`/`fetch` style:
1741
+ * ```ts
1742
+ * app.all("/api/mcp", async (req, res) => {
1743
+ * const r = await agent.mcp.handle({ method: req.method, headers: req.headers, body: req.body });
1744
+ * res.status(r.status).set(r.headers).send(r.body);
1745
+ * });
1746
+ * app.use(async (req, res, next) => {
1747
+ * const doc = await agent.resolvePath(req.path);
1748
+ * if (!doc) return next();
1749
+ * res.status(doc.status).set("Content-Type", doc.contentType).send(doc.body);
1750
+ * });
1751
+ * ```
1752
+ */
1753
+
1754
+ /** Configuration for {@link createAgentStack}. */
1755
+ interface AgentStackConfig {
1756
+ /** BuildBase server base URL. */
1757
+ serverUrl: string;
1758
+ /** The org id. */
1759
+ orgId: string;
1760
+ /** This app's public origin, e.g. `https://app.example.com`. */
1761
+ siteUrl: string;
1762
+ /** Site identity for every discovery document. */
1763
+ site: AgentReadyConfig['site'];
1764
+ /**
1765
+ * The app's token-signing secret — wires `buildbaseAuth` (verify + audience
1766
+ * binding + `sid` decryption) automatically. Never sent anywhere. Omit only
1767
+ * when supplying `mcp.auth` yourself.
1768
+ */
1769
+ secret?: string;
1770
+ /**
1771
+ * The app's OAuth scope catalog (see `AgentReadyConfig.scopes`) —
1772
+ * `scopes_supported` in the protected-resource metadata reads from it.
1773
+ */
1774
+ scopes?: AppScope[];
1775
+ /** MCP server options. All optional — omit the whole block for defaults. */
1776
+ mcp?: {
1777
+ /** Endpoint path on `siteUrl`. Default `/api/mcp`. */
1778
+ path?: string;
1779
+ /** Advertised server identity. Defaults to the site name, v1.0.0. */
1780
+ serverInfo?: {
1781
+ name: string;
1782
+ version: string;
1783
+ };
1784
+ /** Your custom tools. */
1785
+ tools?: McpToolDefinition<any, any>[];
1786
+ /** Built-in tool selection. Default `'readonly'`. */
1787
+ builtinTools?: CreateMcpHandlerConfig['builtinTools'];
1788
+ /** Scopes advertised on the protected-resource metadata. */
1789
+ scopes?: string[];
1790
+ /** Your own BuildBase client; auto-created from serverUrl/orgId if omitted. */
1791
+ buildbase?: McpBuildBaseClient;
1792
+ /** Override the derived auth (e.g. `false` for local dev). */
1793
+ auth?: CreateMcpHandlerConfig['auth'];
1794
+ /** Any remaining `createMcpHandler` option, merged last. */
1795
+ handler?: Partial<CreateMcpHandlerConfig>;
1796
+ };
1797
+ /**
1798
+ * Any {@link AgentReadyConfig} field (robots, skills, llms.txt, security,
1799
+ * a2aCard, extraPaths, …) — merged over the derived discovery config, so
1800
+ * every derived value can be replaced.
1801
+ */
1802
+ discovery?: Partial<AgentReadyConfig>;
1803
+ }
1804
+ /** What {@link createAgentStack} returns. */
1805
+ interface AgentStack {
1806
+ /** The MCP handler (`handle`/`fetch`/`listTools`/`serverCard`). */
1807
+ mcp: McpHandler;
1808
+ /** Absolute MCP endpoint URL. */
1809
+ mcpEndpoint: string;
1810
+ /** The fully derived discovery config (inspect or reuse in middleware). */
1811
+ config: AgentReadyConfig;
1812
+ /** Discovery `Link` header value for your middleware / edge responses. */
1813
+ linkHeader: string;
1814
+ /** Resolve any agent-facing path to a document (framework-agnostic). */
1815
+ resolvePath(path: string): Promise<DiscoveryDocument | null>;
1816
+ /** Web-standard discovery route: hand it any `Request`, get a `Response`. */
1817
+ serveAgentPath(request: Request): Promise<Response>;
1818
+ /**
1819
+ * Web-standard MCP route handlers, ready to re-export from a route file:
1820
+ * `export const { GET, POST, DELETE, OPTIONS } = agent.routes`.
1821
+ */
1822
+ routes: {
1823
+ GET: (request: Request) => Promise<Response>;
1824
+ POST: (request: Request) => Promise<Response>;
1825
+ DELETE: (request: Request) => Promise<Response>;
1826
+ OPTIONS: (request: Request) => Promise<Response>;
1827
+ };
1828
+ }
1829
+ /**
1830
+ * Build the complete agent stack — MCP server + discovery surface — from one
1831
+ * config object. See the module doc for full wiring examples.
1832
+ */
1833
+ declare function createAgentStack(stack: AgentStackConfig): AgentStack;
1834
+
1835
+ /**
1836
+ * BuildBase agent-auth presets (server-side, framework-agnostic).
1837
+ *
1838
+ * The BuildBase security model: the platform runs the OAuth2 flow (login,
1839
+ * consent, PKCE, refresh) but the APP mints and verifies its own user tokens,
1840
+ * signed with the app's own secret. The platform only relays the minted token
1841
+ * to the agent — it never sees the secret, so it can neither forge nor decode
1842
+ * an app token. The per-user BuildBase session travels inside the token only
1843
+ * as an AES-256-GCM-encrypted `sid` claim, so it is never stored and never
1844
+ * plaintext anywhere; access to it expires with the token.
1845
+ *
1846
+ * These presets make that model a one-liner on each side of the flow, and run
1847
+ * entirely in the app's process — pure local crypto, zero platform calls:
1848
+ *
1849
+ * • {@link mintAgentToken} — inside your `applicationTokenUrl` handler:
1850
+ * signs the app JWT (HS256) with your secret, stamps `aud` from the RFC
1851
+ * 8707 resource, and embeds the encrypted `sid` automatically.
1852
+ * • {@link buildbaseAuth} — the `auth` config for `createMcpHandler`:
1853
+ * verifies the JWT, enforces audience binding, decrypts the `sid`, and
1854
+ * maps the claims to `McpAuthInfo`.
1855
+ *
1856
+ * @example The full loop:
1857
+ * ```ts
1858
+ * // applicationTokenUrl route — the platform calls this on every grant
1859
+ * const { status, body } = await handleAppTokenRequest({
1860
+ * authorization: req.headers.get("authorization"),
1861
+ * clientSecret: process.env.BUILDBASE_CLIENT_SECRET!,
1862
+ * mintToken: (claims) => mintAgentToken({ claims, secret: process.env.SYSTEM_SECRET! }),
1863
+ * });
1864
+ *
1865
+ * // MCP server — verifies what the route above minted
1866
+ * const mcp = createMcpHandler({
1867
+ * buildbase,
1868
+ * auth: buildbaseAuth({ secret: process.env.SYSTEM_SECRET!, resource: "https://app.example.com/api/mcp" }),
1869
+ * });
1870
+ * ```
1871
+ *
1872
+ * AES-GCM comes from WebCrypto (`globalThis.crypto.subtle`) — Node 18+, Bun,
1873
+ * Deno, edge runtimes, browsers. The wire format is
1874
+ * `base64url(iv(12) || authTag(16) || ciphertext)` with the key derived as
1875
+ * `SHA-256(secret + ":bb-session")`, byte-compatible with Node
1876
+ * `createCipheriv("aes-256-gcm", ...)` implementations of the same layout.
1877
+ */
1878
+
1879
+ /** Encrypt/decrypt pair for the `sid` claim, bound to one app secret. */
1880
+ interface SessionRefCrypto {
1881
+ /** Encrypt a BuildBase session id for embedding in an app JWT. */
1882
+ encryptSessionRef(sessionId: string): Promise<string>;
1883
+ /** Decrypt a `sid` claim. Returns null on any failure (wrong key, garbage). */
1884
+ decryptSessionRef(ref: string): Promise<string | null>;
1885
+ }
1886
+ /**
1887
+ * Session-ref crypto derived from the app's secret. Both sides of the loop
1888
+ * ({@link mintAgentToken}, {@link buildbaseAuth}) use this internally — reach
1889
+ * for it directly only when minting or verifying tokens by hand.
1890
+ */
1891
+ declare function createSessionRefCrypto(secret: string): SessionRefCrypto;
1892
+ /** Options for {@link mintAgentToken}. */
1893
+ interface MintAgentTokenOptions {
1894
+ /** The verified platform claims (from `handleAppTokenRequest`'s callback). */
1895
+ claims: AppTokenRequestClaims;
1896
+ /** The APP's token-signing secret. Never leaves this process. */
1897
+ secret: string;
1898
+ /** Token lifetime in seconds. Default 3600 — keep agent tokens short-lived. */
1899
+ expiresInSec?: number;
1900
+ /**
1901
+ * App-specific claims merged into the token — e.g. a `workspaceId` resolved
1902
+ * by your own user sync. Merged last, so they can override the defaults.
1903
+ */
1904
+ extraClaims?: Record<string, unknown>;
1905
+ }
1906
+ /**
1907
+ * Mint the app's agent access token from a platform grant: HS256-signed with
1908
+ * YOUR secret, `aud` bound to the granted RFC 8707 resource(s), scopes carried
1909
+ * through, and the per-user BuildBase session embedded as an encrypted `sid`
1910
+ * claim. The result plugs straight into `handleAppTokenRequest`'s `mintToken`.
1911
+ */
1912
+ declare function mintAgentToken(options: MintAgentTokenOptions): Promise<{
1913
+ token: string;
1914
+ expiresIn: number;
1915
+ }>;
1916
+ /** Options for {@link buildbaseAuth}. */
1917
+ interface BuildBaseAuthOptions {
1918
+ /** The APP's token-signing secret — the same one `mintAgentToken` used. */
1919
+ secret: string;
1920
+ /**
1921
+ * The protected resource(s) this server IS. Enforces RFC 8707 audience
1922
+ * binding — the token's `aud` must include one of these URIs. Pass an array
1923
+ * when the server is addressable under more than one resource identifier
1924
+ * (e.g. the canonical `<host>/mcp` plus the literal endpoint URL); the
1925
+ * FIRST entry is the canonical one used to derive `resourceMetadataUrl`.
1926
+ */
1927
+ resource?: string | string[];
1928
+ /**
1929
+ * Explicit RFC 9728 metadata URL for the 401 challenge. Defaults to the
1930
+ * path-derived `/.well-known/oauth-protected-resource<path>` of the first
1931
+ * `resource`.
1932
+ */
1933
+ resourceMetadataUrl?: string;
1934
+ /**
1935
+ * Reject tokens that carry no `aud` claim at all. Default false: audience is
1936
+ * enforced when present, and legacy audience-less tokens still work. Turn
1937
+ * this on once every minted token is audience-bound.
1938
+ */
1939
+ requireAudience?: boolean;
1940
+ /** Extra JWT verification options (clock tolerance, issuer pinning). */
1941
+ verify?: Pick<VerifyClientJwtOptions, 'clockToleranceSec' | 'issuer'>;
1942
+ }
1943
+ /**
1944
+ * The `auth` config for `createMcpHandler`, prewired for BuildBase-minted app
1945
+ * tokens: HS256 verification with your secret, RFC 8707 audience binding,
1946
+ * `sid` decryption to the BuildBase session, claim mapping to `McpAuthInfo`,
1947
+ * and the derived RFC 9728 `resourceMetadataUrl`. Entirely local — the token
1948
+ * and secret never leave this process.
1949
+ */
1950
+ declare function buildbaseAuth(options: BuildBaseAuthOptions): {
1951
+ verify: (token: string, req: McpHttpRequest) => Promise<McpAuthInfo | null>;
1952
+ resourceMetadataUrl?: string;
1953
+ };
1954
+
1955
+ export { AppBridgeError, bearerChallenge, buildbaseAuth, builtinMcpTools, createAgentStack, createMcpHandler, createSessionRefCrypto, defineMcpTool, extractBearerToken, mintAgentToken, selectBuiltinTools, signClientJwt, verifyClientJwt };
1956
+ export type { AgentStack, AgentStackConfig, BuildBaseAuthOptions, BuiltinMcpToolName, CreateMcpHandlerConfig, McpAuthInfo, McpBuildBaseClient, McpHandler, McpHttpRequest, McpHttpResponse, McpServerCard, McpToolAnnotations, McpToolContext, McpToolDefinition, MintAgentTokenOptions, ScopedActions, SessionRefCrypto };