@proveanything/smartlinks 2.0.35 → 2.0.37

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.
package/dist/api/ai.d.ts CHANGED
@@ -111,8 +111,8 @@ declare namespace aiInternal {
111
111
  }>;
112
112
  }
113
113
  /**
114
- * AI usage / cost report for a collection, grouped by any of model/serviceTier/appId/feature/mode
115
- * over an optional date window. `costUnits` are OPAQUE internal units, never provider currency.
114
+ * AI usage for a collection (daily totals), grouped by any of model/appId/feature/mode/surface/provider/day
115
+ * over an optional date window (YYYY-MM-DD). Usage only — requests, tokens, images; no cost figures.
116
116
  */
117
117
  function usage(collectionId: string, params?: {
118
118
  groupBy?: string | string[];
package/dist/api/ai.js CHANGED
@@ -224,8 +224,8 @@ var aiInternal;
224
224
  sessions.clear = clear;
225
225
  })(sessions = aiInternal.sessions || (aiInternal.sessions = {}));
226
226
  /**
227
- * AI usage / cost report for a collection, grouped by any of model/serviceTier/appId/feature/mode
228
- * over an optional date window. `costUnits` are OPAQUE internal units, never provider currency.
227
+ * AI usage for a collection (daily totals), grouped by any of model/appId/feature/mode/surface/provider/day
228
+ * over an optional date window (YYYY-MM-DD). Usage only — requests, tokens, images; no cost figures.
229
229
  */
230
230
  async function usage(collectionId, params) {
231
231
  const groupBy = Array.isArray(params === null || params === void 0 ? void 0 : params.groupBy) ? params.groupBy.join(',') : params === null || params === void 0 ? void 0 : params.groupBy;
@@ -27,7 +27,7 @@ export declare namespace collection {
27
27
  * The server derives the requesting domain from the request headers
28
28
  * (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is
29
29
  * passed — this is the call a Hub frontend makes on load to find out which
30
- * collection it is serving, whether it's reached via `{brand}.mysmartlinks.app`
30
+ * collection it is serving, whether it's reached via `{brand}.smartlinks.host`
31
31
  * or a bring-your-own custom domain (e.g. `hub.acme.com`).
32
32
  *
33
33
  * @returns Promise resolving to the CollectionResponse mapped to the domain
@@ -39,9 +39,9 @@ export declare namespace collection {
39
39
  *
40
40
  * Unlike {@link getByHub}, the domain is passed explicitly rather than derived
41
41
  * from request headers — use this for raw/cross-origin calls where the Hub
42
- * frontend knows its own hostname (e.g. "erbauer.mysmartlinks.app").
42
+ * frontend knows its own hostname (e.g. "erbauer.smartlinks.host").
43
43
  *
44
- * @param domain – The Hub domain to resolve (custom domain or {brand}.mysmartlinks.app)
44
+ * @param domain – The Hub domain to resolve (custom domain or {brand}.smartlinks.host)
45
45
  * @returns Promise resolving to the CollectionResponse mapped to the domain
46
46
  * @throws ErrorResponse (404) if no collection is mapped to the domain
47
47
  */
@@ -57,7 +57,7 @@ export declare namespace collection {
57
57
  /**
58
58
  * Claim or rename the Hub subdomain for a collection (admin only).
59
59
  *
60
- * Maps `{hubName}.mysmartlinks.app` to the collection. If the collection
60
+ * Maps `{hubName}.smartlinks.host` to the collection. If the collection
61
61
  * already had a different hub name, the previous subdomain is released
62
62
  * automatically.
63
63
  *
@@ -43,7 +43,7 @@ export var collection;
43
43
  * The server derives the requesting domain from the request headers
44
44
  * (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is
45
45
  * passed — this is the call a Hub frontend makes on load to find out which
46
- * collection it is serving, whether it's reached via `{brand}.mysmartlinks.app`
46
+ * collection it is serving, whether it's reached via `{brand}.smartlinks.host`
47
47
  * or a bring-your-own custom domain (e.g. `hub.acme.com`).
48
48
  *
49
49
  * @returns Promise resolving to the CollectionResponse mapped to the domain
@@ -59,9 +59,9 @@ export var collection;
59
59
  *
60
60
  * Unlike {@link getByHub}, the domain is passed explicitly rather than derived
61
61
  * from request headers — use this for raw/cross-origin calls where the Hub
62
- * frontend knows its own hostname (e.g. "erbauer.mysmartlinks.app").
62
+ * frontend knows its own hostname (e.g. "erbauer.smartlinks.host").
63
63
  *
64
- * @param domain – The Hub domain to resolve (custom domain or {brand}.mysmartlinks.app)
64
+ * @param domain – The Hub domain to resolve (custom domain or {brand}.smartlinks.host)
65
65
  * @returns Promise resolving to the CollectionResponse mapped to the domain
66
66
  * @throws ErrorResponse (404) if no collection is mapped to the domain
67
67
  */
@@ -86,7 +86,7 @@ export var collection;
86
86
  /**
87
87
  * Claim or rename the Hub subdomain for a collection (admin only).
88
88
  *
89
- * Maps `{hubName}.mysmartlinks.app` to the collection. If the collection
89
+ * Maps `{hubName}.smartlinks.host` to the collection. If the collection
90
90
  * already had a different hub name, the previous subdomain is released
91
91
  * automatically.
92
92
  *
@@ -29,7 +29,37 @@ export interface FunctionCallOptions {
29
29
  export declare function resolveFunctionChannel(opts?: FunctionCallOptions, appId?: string): string | undefined;
30
30
  /** The API path a function call goes to (exported for hosts/tests that need the exact URL). */
31
31
  export declare function functionPath(surface: 'public' | 'admin', collectionId: string, name: string, opts?: FunctionCallOptions): string;
32
+ /** Options for {@link functions.siteUrl}. */
33
+ export interface FunctionSiteUrlOptions {
34
+ /** Release channel ('dev' | 'alpha' | 'beta' | 'stable'); omit for the collection's installed release. */
35
+ channel?: string;
36
+ /** Sub-path after the function name, e.g. "/orders/123" (the function must declare trigger.path). */
37
+ path?: string;
38
+ /** Query parameters to append. */
39
+ query?: Record<string, string>;
40
+ /** Use this host instead of the collection's siteHost (e.g. its connected custom domain). */
41
+ host?: string;
42
+ }
32
43
  export declare namespace functions {
44
+ /**
45
+ * The PUBLIC address of an app function on the collection's own site — what you give a third party
46
+ * as a webhook URL, or call from the collection's public pages:
47
+ * `https://<siteHost>/_fn/<appId>[/<channel>]/<name>[/<path>]`.
48
+ * Every HTTP method the function declares works there, with the raw body for signature checks.
49
+ * Pass the collection (or its siteHost). This address is for public/integration calls; signed-in
50
+ * calls from your app keep using {@link call} / {@link callAdmin}, so the user's SmartLinks session
51
+ * never goes to a tenant hostname.
52
+ *
53
+ * @example
54
+ * const col = await SL.collection.get(collectionId)
55
+ * const hookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' })
56
+ * // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook
57
+ */
58
+ function siteUrl(collection: {
59
+ siteHost?: string | null;
60
+ } | string, name: string, opts?: FunctionSiteUrlOptions & {
61
+ appId?: string;
62
+ }): string;
33
63
  /**
34
64
  * Call a PUBLIC app server function inline (surface `'public'`).
35
65
  * App-scoped: `POST /public/collection/:c/app/:appId/functions/:name`.
@@ -64,6 +64,34 @@ export function functionPath(surface, collectionId, name, opts = {}) {
64
64
  const fnPath = functionPath;
65
65
  export var functions;
66
66
  (function (functions) {
67
+ /**
68
+ * The PUBLIC address of an app function on the collection's own site — what you give a third party
69
+ * as a webhook URL, or call from the collection's public pages:
70
+ * `https://<siteHost>/_fn/<appId>[/<channel>]/<name>[/<path>]`.
71
+ * Every HTTP method the function declares works there, with the raw body for signature checks.
72
+ * Pass the collection (or its siteHost). This address is for public/integration calls; signed-in
73
+ * calls from your app keep using {@link call} / {@link callAdmin}, so the user's SmartLinks session
74
+ * never goes to a tenant hostname.
75
+ *
76
+ * @example
77
+ * const col = await SL.collection.get(collectionId)
78
+ * const hookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' })
79
+ * // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook
80
+ */
81
+ function siteUrl(collection, name, opts = {}) {
82
+ var _a, _b;
83
+ const host = opts.host || (typeof collection === 'string' ? collection : collection && collection.siteHost);
84
+ if (!host)
85
+ throw new Error('functions.siteUrl: the collection has no siteHost (fetch it with SL.collection.get)');
86
+ const app = (_a = opts.appId) !== null && _a !== void 0 ? _a : getAppContext();
87
+ if (!app)
88
+ throw new Error('functions.siteUrl: appId required (pass it, or initializeApi({ appId }))');
89
+ const ch = resolveFunctionChannel({ channel: (_b = opts.channel) !== null && _b !== void 0 ? _b : null }, app);
90
+ const sub = opts.path ? '/' + String(opts.path).replace(/^\/+/, '') : '';
91
+ const qs = opts.query && Object.keys(opts.query).length ? '?' + new URLSearchParams(opts.query).toString() : '';
92
+ return `https://${String(host).replace(/^https?:\/\//, '').replace(/\/+$/, '')}/_fn/${encodeURIComponent(app)}${ch ? `/${ch}` : ''}/${encodeURIComponent(name)}${sub}${qs}`;
93
+ }
94
+ functions.siteUrl = siteUrl;
67
95
  /**
68
96
  * Call a PUBLIC app server function inline (surface `'public'`).
69
97
  * App-scoped: `POST /public/collection/:c/app/:appId/functions/:name`.
package/dist/context.js CHANGED
@@ -40,6 +40,21 @@ export function readContext(overrides) {
40
40
  if (window.location.search)
41
41
  readSearchParamsInto(out, new URLSearchParams(window.location.search));
42
42
  }
43
+ // 4. the SITE the page is served as (lowest precedence). When an app runs as a public website at
44
+ // its own hostname (an "app site"), the host page injects window.__SMARTLINKS_SITE__ =
45
+ // { collectionId, appId, channel, host } — so a site knows its collection with no URL params.
46
+ const site = globalThis.__SMARTLINKS_SITE__;
47
+ if (site && typeof site === 'object') {
48
+ const fill = (key, value) => { if (out[key] == null && typeof value === 'string' && value)
49
+ out[key] = value; };
50
+ fill('collectionId', site.collectionId);
51
+ fill('appId', site.appId);
52
+ // A site on a pre-release channel calls that channel's server functions (stable needs no channel).
53
+ if (site.channel && site.channel !== 'stable') {
54
+ fill('appChannel', site.channel);
55
+ fill('appChannelApp', site.appId);
56
+ }
57
+ }
43
58
  }
44
59
  catch (_a) {
45
60
  /* non-browser / bad URL — return what we have */
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.0.35 | Generated: 2026-10-03T12:37:51.058Z
3
+ Version: 2.0.37 | Generated: 2026-10-04T09:59:59.885Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -918,8 +918,20 @@ interface AiUsageReport {
918
918
  groupBy: string[]
919
919
  from?: string | null
920
920
  to?: string | null
921
- totals: { promptTokens: number; outputTokens: number; totalTokens: number; requests: number; costUnits: number }
922
- groups: Array<Record<string, any> & { promptTokens: number; outputTokens: number; totalTokens: number; requests: number; costUnits: number }>
921
+ totals: AiUsageTotals
922
+ groups: Array<Record<string, any> & AiUsageTotals>
923
+ }
924
+ ```
925
+
926
+ **AiUsageTotals** (interface)
927
+ ```typescript
928
+ interface AiUsageTotals {
929
+ promptTokens: number
930
+ outputTokens: number
931
+ cachedTokens: number
932
+ totalTokens: number
933
+ requests: number
934
+ images: number
923
935
  }
924
936
  ```
925
937
 
@@ -4839,6 +4851,7 @@ interface Collection {
4839
4851
  redirectUrl?: string // Whether the collection has a custom domain
4840
4852
  hubName?: string
4841
4853
  hubCustomDomain?: string
4854
+ siteHost?: string | null
4842
4855
  shortId: string, // The shortId of this collection
4843
4856
  dark?: boolean // if dark mode is enabled for this collection
4844
4857
  primaryColor?: string
@@ -9594,6 +9607,16 @@ interface FunctionCallOptions {
9594
9607
  }
9595
9608
  ```
9596
9609
 
9610
+ **FunctionSiteUrlOptions** (interface)
9611
+ ```typescript
9612
+ interface FunctionSiteUrlOptions {
9613
+ channel?: string
9614
+ path?: string
9615
+ query?: Record<string, string>
9616
+ host?: string
9617
+ }
9618
+ ```
9619
+
9597
9620
  **FunctionCallResult** = `any`
9598
9621
 
9599
9622
  ### sequence (api)
@@ -10722,16 +10745,16 @@ Retrieves all Collections.
10722
10745
  Retrieve a collection by its shortId (public endpoint).
10723
10746
 
10724
10747
  **getByHub**() → `Promise<CollectionResponse>`
10725
- Resolve the collection for the current Hub domain (public endpoint). The server derives the requesting domain from the request headers (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is passed — this is the call a Hub frontend makes on load to find out which collection it is serving, whether it's reached via `{brand}.mysmartlinks.app` or a bring-your-own custom domain (e.g. `hub.acme.com`).
10748
+ Resolve the collection for the current Hub domain (public endpoint). The server derives the requesting domain from the request headers (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is passed — this is the call a Hub frontend makes on load to find out which collection it is serving, whether it's reached via `{brand}.smartlinks.host` or a bring-your-own custom domain (e.g. `hub.acme.com`).
10726
10749
 
10727
10750
  **getByDomain**(domain: string) → `Promise<CollectionResponse>`
10728
- Resolve the collection for an explicit Hub domain (public endpoint). Unlike {@link getByHub}, the domain is passed explicitly rather than derived from request headers — use this for raw/cross-origin calls where the Hub frontend knows its own hostname (e.g. "erbauer.mysmartlinks.app").
10751
+ Resolve the collection for an explicit Hub domain (public endpoint). Unlike {@link getByHub}, the domain is passed explicitly rather than derived from request headers — use this for raw/cross-origin calls where the Hub frontend knows its own hostname (e.g. "erbauer.smartlinks.host").
10729
10752
 
10730
10753
  **checkHubAvailability**(collectionId: string, name: string) → `Promise<HubAvailabilityResponse>`
10731
10754
  Check whether a Hub subdomain name is available to claim (admin only).
10732
10755
 
10733
10756
  **claimHub**(collectionId: string, hubName: string) → `Promise<CollectionResponse>`
10734
- Claim or rename the Hub subdomain for a collection (admin only). Maps `{hubName}.mysmartlinks.app` to the collection. If the collection already had a different hub name, the previous subdomain is released automatically.
10757
+ Claim or rename the Hub subdomain for a collection (admin only). Maps `{hubName}.smartlinks.host` to the collection. If the collection already had a different hub name, the previous subdomain is released automatically.
10735
10758
 
10736
10759
  **registerDomain**(collectionId: string, domain: string, target: DomainTarget = "smartlinks") → `Promise<any>`
10737
10760
  Register a custom domain for a collection and provision its managed certificate (admin only). `"smartlinks"` (the id.smartlinks.app load balancer). Pass `"hub"` to register a bring-your-own Hub domain.
@@ -11108,6 +11131,11 @@ The release channel a call targets, or undefined for "the collection's installed
11108
11131
  **functionPath**(surface: 'public' | 'admin', collectionId: string, name: string, opts: FunctionCallOptions = {}) → `string`
11109
11132
  The API path a function call goes to (exported for hosts/tests that need the exact URL).
11110
11133
 
11134
+ **siteUrl**(collection: { siteHost?: string | null } | string,
11135
+ name: string,
11136
+ opts: FunctionSiteUrlOptions & { appId?: string } = {}) → `string`
11137
+ The PUBLIC address of an app function on the collection's own site — what you give a third party as a webhook URL, or call from the collection's public pages: `https://<siteHost>/_fn/<appId>[/<channel>]/<name>[/<path>]`. Every HTTP method the function declares works there, with the raw body for signature checks. Pass the collection (or its siteHost). This address is for public/integration calls; signed-in calls from your app keep using {@link call} / {@link callAdmin}, so the user's SmartLinks session never goes to a tenant hostname. const col = await SL.collection.get(collectionId) const hookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' }) // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook
11138
+
11111
11139
  **call**(collectionId: string,
11112
11140
  name: string,
11113
11141
  body: Record<string, any> = {},
@@ -251,10 +251,15 @@ await SL.functions.call(collectionId, 'pressCounter', {}, { appId: 'some-other-a
251
251
  **Where it runs.** A function runs on a collection when the app is **installed** there (enabled in
252
252
  the collection's apps); or the app is **restricted** to a list of collections (what publishing from
253
253
  Forge sets for every developer) and this collection is on it, e.g. your own sandbox; or the app is
254
- **public**, meaning a registered, non-development app with no restriction list, which only platform
255
- admins can publish. Anything else gets `404 APP_NOT_INSTALLED`: a function can run with the
256
- collection's own authority and secrets, so a developer's app can't reach a collection that hasn't
257
- taken it on.
254
+ **public**, meaning a registered app with no restriction list, which only platform admins can
255
+ publish. Anything else gets `404 APP_NOT_INSTALLED`: a function can run with the collection's own
256
+ authority and secrets, so a developer's app can't reach a collection that hasn't taken it on.
257
+
258
+ **Who may run server code at all.** Until functions move to an isolated runner, only TRUSTED apps
259
+ execute: public (unrestricted) apps, or apps a platform admin has marked `functionsTrusted: true`.
260
+ Developer apps — restricted to their own collections, which includes every app published from
261
+ self-serve Forge — get `403 FUNCTIONS_NOT_ENABLED` until they're trusted. A platform admin can also
262
+ switch any app's functions off with `functionsTrusted: false`.
258
263
 
259
264
  **Which release.** With no channel, the server runs the release the collection has **installed** (stable for a
260
265
  public app that isn't installed) — what production wants, so app code just calls `SL.functions.call(collectionId, name)`. To run
@@ -269,6 +274,40 @@ await SL.functions.call(collectionId, 'pressCounter', {}, { channel: 'beta' }) /
269
274
  await SL.functions.call(collectionId, 'pressCounter', {}, { channel: null }) // force the installed release
270
275
  ```
271
276
 
277
+ **Public address on the collection's own site (webhooks, integrations).** Every collection has a
278
+ site host — `<name>.smartlinks.host` once a name is claimed, else `c-<collectionId>.smartlinks.host` —
279
+ returned as `collection.siteHost`. App functions are reachable there:
280
+
281
+ ```
282
+ https://<siteHost>/_fn/<appId>[/<channel>]/<function>[/<sub-path…>]
283
+ ```
284
+
285
+ ```ts
286
+ const col = await SL.collection.get(collectionId)
287
+ const webhookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' })
288
+ // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook (give this to Stripe)
289
+ ```
290
+
291
+ Built for integrations: **every HTTP method** the function declares in `trigger.methods` (default
292
+ POST only), **any content type** with the exact bytes in `event.rawBody` (verify signatures with
293
+ `crypto.subtle`), **sub-paths** after the name when the function declares `trigger.path` (`"/*"`, or a
294
+ pattern like `"/orders/:id"` → `event.params.id`), and the function's own `Response` — status, headers,
295
+ CORS — goes back unchanged (the platform adds no CORS headers there; declare `OPTIONS` and answer
296
+ preflights yourself if browsers call you cross-origin). Bodies up to 6 MB.
297
+
298
+ ```js
299
+ // manifest: { name: 'orders', trigger: { type: 'http', methods: ['GET', 'PUT'], path: '/orders/:id' }, visibility: 'public' }
300
+ export async function orders(ctx, event) {
301
+ if (event.method === 'GET') return ctx.sl.appRecords.get(event.params.id)
302
+ // PUT: verify the sender first — e.g. an HMAC over event.rawBody with a secret
303
+ }
304
+ ```
305
+
306
+ Security: the platform never hands a caller's SmartLinks credential to function code (if it signed
307
+ the caller in from `Authorization`, that header is removed and you get `ctx.caller`); your OWN
308
+ `Authorization` scheme for webhooks passes through. `siteUrl` only includes a channel you ask for —
309
+ point production webhooks at the bare address and test ones at `/dev/`.
310
+
272
311
  The channel is never a query parameter: the function owns its query string (`?channel=sms` reaches
273
312
  your handler untouched), and a configured URL — a webhook, a third-party callback — can only ever
274
313
  hit the channel it names. Point production webhooks at the bare path and test ones at `/dev/`.
package/dist/openapi.yaml CHANGED
@@ -1347,7 +1347,7 @@ paths:
1347
1347
  get:
1348
1348
  tags:
1349
1349
  - sessions
1350
- summary: AI usage / cost report for a collection, grouped by any of model/serviceTier/appId/feature/mode over an optional date window.
1350
+ summary: AI usage for a collection (daily totals), grouped by any of model/appId/feature/mode/surface/provider/day over an optional date window (YYYY-MM-DD).
1351
1351
  operationId: sessions_usage
1352
1352
  security:
1353
1353
  - bearerAuth: []
@@ -16327,17 +16327,37 @@ components:
16327
16327
  to:
16328
16328
  type: string
16329
16329
  totals:
16330
- type: object
16331
- additionalProperties: true
16330
+ $ref: "#/components/schemas/AiUsageTotals"
16332
16331
  groups:
16333
16332
  type: array
16334
16333
  items:
16335
- type: object
16336
- additionalProperties: true
16334
+ $ref: "#/components/schemas/AiUsageTotals"
16337
16335
  required:
16338
16336
  - groupBy
16339
16337
  - totals
16340
16338
  - groups
16339
+ AiUsageTotals:
16340
+ type: object
16341
+ properties:
16342
+ promptTokens:
16343
+ type: number
16344
+ outputTokens:
16345
+ type: number
16346
+ cachedTokens:
16347
+ type: number
16348
+ totalTokens:
16349
+ type: number
16350
+ requests:
16351
+ type: number
16352
+ images:
16353
+ type: number
16354
+ required:
16355
+ - promptTokens
16356
+ - outputTokens
16357
+ - cachedTokens
16358
+ - totalTokens
16359
+ - requests
16360
+ - images
16341
16361
  VoiceSessionRequest:
16342
16362
  type: object
16343
16363
  properties:
@@ -23052,6 +23072,8 @@ components:
23052
23072
  type: string
23053
23073
  hubCustomDomain:
23054
23074
  type: string
23075
+ siteHost:
23076
+ type: string
23055
23077
  shortId:
23056
23078
  type: string
23057
23079
  dark:
@@ -30539,6 +30561,19 @@ components:
30539
30561
  appId:
30540
30562
  type: object
30541
30563
  additionalProperties: true
30564
+ FunctionSiteUrlOptions:
30565
+ type: object
30566
+ properties:
30567
+ channel:
30568
+ type: string
30569
+ path:
30570
+ type: string
30571
+ query:
30572
+ type: object
30573
+ additionalProperties:
30574
+ type: string
30575
+ host:
30576
+ type: string
30542
30577
  AllocateSequenceInput:
30543
30578
  type: object
30544
30579
  properties:
@@ -412,25 +412,22 @@ export interface AiSessionCreate {
412
412
  /** Seed items (e.g. a system/instruction turn). */
413
413
  items?: ResponseInputItem[];
414
414
  }
415
- /** Per-collection AI usage/cost report. `costUnits` are OPAQUE internal units, never provider currency. */
415
+ /** Per-collection AI usage report (daily totals). Usage only — requests, tokens, images; no cost figures. */
416
416
  export interface AiUsageReport {
417
417
  groupBy: string[];
418
418
  from?: string | null;
419
419
  to?: string | null;
420
- totals: {
421
- promptTokens: number;
422
- outputTokens: number;
423
- totalTokens: number;
424
- requests: number;
425
- costUnits: number;
426
- };
427
- groups: Array<Record<string, any> & {
428
- promptTokens: number;
429
- outputTokens: number;
430
- totalTokens: number;
431
- requests: number;
432
- costUnits: number;
433
- }>;
420
+ totals: AiUsageTotals;
421
+ groups: Array<Record<string, any> & AiUsageTotals>;
422
+ }
423
+ /** Summed usage for a report group (or the whole window). */
424
+ export interface AiUsageTotals {
425
+ promptTokens: number;
426
+ outputTokens: number;
427
+ cachedTokens: number;
428
+ totalTokens: number;
429
+ requests: number;
430
+ images: number;
434
431
  }
435
432
  /** Voice session request */
436
433
  export interface VoiceSessionRequest {
@@ -42,10 +42,17 @@ export interface Collection {
42
42
  groupTags?: string[];
43
43
  /** Whether the collection has a custom domain */
44
44
  redirectUrl?: string;
45
- /** The claimed Hub subdomain prefix (e.g. "acme" → acme.mysmartlinks.app) */
45
+ /** The claimed Hub subdomain prefix (e.g. "acme" → acme.smartlinks.host) */
46
46
  hubName?: string;
47
47
  /** The collection's bring-your-own custom Hub domain (e.g. "hub.acme.com") */
48
48
  hubCustomDomain?: string;
49
+ /**
50
+ * The collection's SITE HOST on the tenant domain — computed by the server, never stored:
51
+ * `<hubName>.smartlinks.host` when a name is claimed, else the default `c-<collectionId>.smartlinks.host`.
52
+ * Hub serves it, and app functions are reachable under it at `https://<siteHost>/_fn/…`
53
+ * (see SL.functions.siteUrl).
54
+ */
55
+ siteHost?: string | null;
49
56
  /** The shortId of this collection */
50
57
  shortId: string;
51
58
  /** if dark mode is enabled for this collection */
@@ -97,7 +104,7 @@ export type DomainTarget = "smartlinks" | "hub";
97
104
  export interface HubAvailabilityResponse {
98
105
  /** Whether the name can be claimed by this collection */
99
106
  available: boolean;
100
- /** The full domain that was checked (e.g. "acme.mysmartlinks.app") */
107
+ /** The full domain that was checked (e.g. "acme.smartlinks.host") */
101
108
  domain: string;
102
109
  }
103
110
  /**
@@ -2,7 +2,7 @@
2
2
  // URL on any of these is NOT a custom domain, so non-master GTINs still need the
3
3
  // `/gc/{shortId}` collection prefix. `portalUrl` is only ever set to the platform
4
4
  // default or a collection's custom domain, so exact-host matching is sufficient here.
5
- const PLATFORM_HOSTS = ['smartlinks.app', 'mysmartlinks.app', 'zt.smartlinks.io'];
5
+ const PLATFORM_HOSTS = ['smartlinks.app', 'smartlinks.host', 'mysmartlinks.app', 'zt.smartlinks.io'];
6
6
  /** True when `baseUrl`'s host is a collection's own custom domain (not a platform host). */
7
7
  function baseIsCustomDomain(baseUrl) {
8
8
  if (!baseUrl)
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.0.35 | Generated: 2026-10-03T12:37:51.058Z
3
+ Version: 2.0.37 | Generated: 2026-10-04T09:59:59.885Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -918,8 +918,20 @@ interface AiUsageReport {
918
918
  groupBy: string[]
919
919
  from?: string | null
920
920
  to?: string | null
921
- totals: { promptTokens: number; outputTokens: number; totalTokens: number; requests: number; costUnits: number }
922
- groups: Array<Record<string, any> & { promptTokens: number; outputTokens: number; totalTokens: number; requests: number; costUnits: number }>
921
+ totals: AiUsageTotals
922
+ groups: Array<Record<string, any> & AiUsageTotals>
923
+ }
924
+ ```
925
+
926
+ **AiUsageTotals** (interface)
927
+ ```typescript
928
+ interface AiUsageTotals {
929
+ promptTokens: number
930
+ outputTokens: number
931
+ cachedTokens: number
932
+ totalTokens: number
933
+ requests: number
934
+ images: number
923
935
  }
924
936
  ```
925
937
 
@@ -4839,6 +4851,7 @@ interface Collection {
4839
4851
  redirectUrl?: string // Whether the collection has a custom domain
4840
4852
  hubName?: string
4841
4853
  hubCustomDomain?: string
4854
+ siteHost?: string | null
4842
4855
  shortId: string, // The shortId of this collection
4843
4856
  dark?: boolean // if dark mode is enabled for this collection
4844
4857
  primaryColor?: string
@@ -9594,6 +9607,16 @@ interface FunctionCallOptions {
9594
9607
  }
9595
9608
  ```
9596
9609
 
9610
+ **FunctionSiteUrlOptions** (interface)
9611
+ ```typescript
9612
+ interface FunctionSiteUrlOptions {
9613
+ channel?: string
9614
+ path?: string
9615
+ query?: Record<string, string>
9616
+ host?: string
9617
+ }
9618
+ ```
9619
+
9597
9620
  **FunctionCallResult** = `any`
9598
9621
 
9599
9622
  ### sequence (api)
@@ -10722,16 +10745,16 @@ Retrieves all Collections.
10722
10745
  Retrieve a collection by its shortId (public endpoint).
10723
10746
 
10724
10747
  **getByHub**() → `Promise<CollectionResponse>`
10725
- Resolve the collection for the current Hub domain (public endpoint). The server derives the requesting domain from the request headers (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is passed — this is the call a Hub frontend makes on load to find out which collection it is serving, whether it's reached via `{brand}.mysmartlinks.app` or a bring-your-own custom domain (e.g. `hub.acme.com`).
10748
+ Resolve the collection for the current Hub domain (public endpoint). The server derives the requesting domain from the request headers (`X-Source-Domain` / `X-Forwarded-Host` / `Host`), so no identifier is passed — this is the call a Hub frontend makes on load to find out which collection it is serving, whether it's reached via `{brand}.smartlinks.host` or a bring-your-own custom domain (e.g. `hub.acme.com`).
10726
10749
 
10727
10750
  **getByDomain**(domain: string) → `Promise<CollectionResponse>`
10728
- Resolve the collection for an explicit Hub domain (public endpoint). Unlike {@link getByHub}, the domain is passed explicitly rather than derived from request headers — use this for raw/cross-origin calls where the Hub frontend knows its own hostname (e.g. "erbauer.mysmartlinks.app").
10751
+ Resolve the collection for an explicit Hub domain (public endpoint). Unlike {@link getByHub}, the domain is passed explicitly rather than derived from request headers — use this for raw/cross-origin calls where the Hub frontend knows its own hostname (e.g. "erbauer.smartlinks.host").
10729
10752
 
10730
10753
  **checkHubAvailability**(collectionId: string, name: string) → `Promise<HubAvailabilityResponse>`
10731
10754
  Check whether a Hub subdomain name is available to claim (admin only).
10732
10755
 
10733
10756
  **claimHub**(collectionId: string, hubName: string) → `Promise<CollectionResponse>`
10734
- Claim or rename the Hub subdomain for a collection (admin only). Maps `{hubName}.mysmartlinks.app` to the collection. If the collection already had a different hub name, the previous subdomain is released automatically.
10757
+ Claim or rename the Hub subdomain for a collection (admin only). Maps `{hubName}.smartlinks.host` to the collection. If the collection already had a different hub name, the previous subdomain is released automatically.
10735
10758
 
10736
10759
  **registerDomain**(collectionId: string, domain: string, target: DomainTarget = "smartlinks") → `Promise<any>`
10737
10760
  Register a custom domain for a collection and provision its managed certificate (admin only). `"smartlinks"` (the id.smartlinks.app load balancer). Pass `"hub"` to register a bring-your-own Hub domain.
@@ -11108,6 +11131,11 @@ The release channel a call targets, or undefined for "the collection's installed
11108
11131
  **functionPath**(surface: 'public' | 'admin', collectionId: string, name: string, opts: FunctionCallOptions = {}) → `string`
11109
11132
  The API path a function call goes to (exported for hosts/tests that need the exact URL).
11110
11133
 
11134
+ **siteUrl**(collection: { siteHost?: string | null } | string,
11135
+ name: string,
11136
+ opts: FunctionSiteUrlOptions & { appId?: string } = {}) → `string`
11137
+ The PUBLIC address of an app function on the collection's own site — what you give a third party as a webhook URL, or call from the collection's public pages: `https://<siteHost>/_fn/<appId>[/<channel>]/<name>[/<path>]`. Every HTTP method the function declares works there, with the raw body for signature checks. Pass the collection (or its siteHost). This address is for public/integration calls; signed-in calls from your app keep using {@link call} / {@link callAdmin}, so the user's SmartLinks session never goes to a tenant hostname. const col = await SL.collection.get(collectionId) const hookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' }) // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook
11138
+
11111
11139
  **call**(collectionId: string,
11112
11140
  name: string,
11113
11141
  body: Record<string, any> = {},
@@ -251,10 +251,15 @@ await SL.functions.call(collectionId, 'pressCounter', {}, { appId: 'some-other-a
251
251
  **Where it runs.** A function runs on a collection when the app is **installed** there (enabled in
252
252
  the collection's apps); or the app is **restricted** to a list of collections (what publishing from
253
253
  Forge sets for every developer) and this collection is on it, e.g. your own sandbox; or the app is
254
- **public**, meaning a registered, non-development app with no restriction list, which only platform
255
- admins can publish. Anything else gets `404 APP_NOT_INSTALLED`: a function can run with the
256
- collection's own authority and secrets, so a developer's app can't reach a collection that hasn't
257
- taken it on.
254
+ **public**, meaning a registered app with no restriction list, which only platform admins can
255
+ publish. Anything else gets `404 APP_NOT_INSTALLED`: a function can run with the collection's own
256
+ authority and secrets, so a developer's app can't reach a collection that hasn't taken it on.
257
+
258
+ **Who may run server code at all.** Until functions move to an isolated runner, only TRUSTED apps
259
+ execute: public (unrestricted) apps, or apps a platform admin has marked `functionsTrusted: true`.
260
+ Developer apps — restricted to their own collections, which includes every app published from
261
+ self-serve Forge — get `403 FUNCTIONS_NOT_ENABLED` until they're trusted. A platform admin can also
262
+ switch any app's functions off with `functionsTrusted: false`.
258
263
 
259
264
  **Which release.** With no channel, the server runs the release the collection has **installed** (stable for a
260
265
  public app that isn't installed) — what production wants, so app code just calls `SL.functions.call(collectionId, name)`. To run
@@ -269,6 +274,40 @@ await SL.functions.call(collectionId, 'pressCounter', {}, { channel: 'beta' }) /
269
274
  await SL.functions.call(collectionId, 'pressCounter', {}, { channel: null }) // force the installed release
270
275
  ```
271
276
 
277
+ **Public address on the collection's own site (webhooks, integrations).** Every collection has a
278
+ site host — `<name>.smartlinks.host` once a name is claimed, else `c-<collectionId>.smartlinks.host` —
279
+ returned as `collection.siteHost`. App functions are reachable there:
280
+
281
+ ```
282
+ https://<siteHost>/_fn/<appId>[/<channel>]/<function>[/<sub-path…>]
283
+ ```
284
+
285
+ ```ts
286
+ const col = await SL.collection.get(collectionId)
287
+ const webhookUrl = SL.functions.siteUrl(col, 'stripeWebhook', { appId: 'my-shop' })
288
+ // → https://acme.smartlinks.host/_fn/my-shop/stripeWebhook (give this to Stripe)
289
+ ```
290
+
291
+ Built for integrations: **every HTTP method** the function declares in `trigger.methods` (default
292
+ POST only), **any content type** with the exact bytes in `event.rawBody` (verify signatures with
293
+ `crypto.subtle`), **sub-paths** after the name when the function declares `trigger.path` (`"/*"`, or a
294
+ pattern like `"/orders/:id"` → `event.params.id`), and the function's own `Response` — status, headers,
295
+ CORS — goes back unchanged (the platform adds no CORS headers there; declare `OPTIONS` and answer
296
+ preflights yourself if browsers call you cross-origin). Bodies up to 6 MB.
297
+
298
+ ```js
299
+ // manifest: { name: 'orders', trigger: { type: 'http', methods: ['GET', 'PUT'], path: '/orders/:id' }, visibility: 'public' }
300
+ export async function orders(ctx, event) {
301
+ if (event.method === 'GET') return ctx.sl.appRecords.get(event.params.id)
302
+ // PUT: verify the sender first — e.g. an HMAC over event.rawBody with a secret
303
+ }
304
+ ```
305
+
306
+ Security: the platform never hands a caller's SmartLinks credential to function code (if it signed
307
+ the caller in from `Authorization`, that header is removed and you get `ctx.caller`); your OWN
308
+ `Authorization` scheme for webhooks passes through. `siteUrl` only includes a channel you ask for —
309
+ point production webhooks at the bare address and test ones at `/dev/`.
310
+
272
311
  The channel is never a query parameter: the function owns its query string (`?channel=sms` reaches
273
312
  your handler untouched), and a configured URL — a webhook, a third-party callback — can only ever
274
313
  hit the channel it names. Point production webhooks at the bare path and test ones at `/dev/`.
package/openapi.yaml CHANGED
@@ -1347,7 +1347,7 @@ paths:
1347
1347
  get:
1348
1348
  tags:
1349
1349
  - sessions
1350
- summary: AI usage / cost report for a collection, grouped by any of model/serviceTier/appId/feature/mode over an optional date window.
1350
+ summary: AI usage for a collection (daily totals), grouped by any of model/appId/feature/mode/surface/provider/day over an optional date window (YYYY-MM-DD).
1351
1351
  operationId: sessions_usage
1352
1352
  security:
1353
1353
  - bearerAuth: []
@@ -16327,17 +16327,37 @@ components:
16327
16327
  to:
16328
16328
  type: string
16329
16329
  totals:
16330
- type: object
16331
- additionalProperties: true
16330
+ $ref: "#/components/schemas/AiUsageTotals"
16332
16331
  groups:
16333
16332
  type: array
16334
16333
  items:
16335
- type: object
16336
- additionalProperties: true
16334
+ $ref: "#/components/schemas/AiUsageTotals"
16337
16335
  required:
16338
16336
  - groupBy
16339
16337
  - totals
16340
16338
  - groups
16339
+ AiUsageTotals:
16340
+ type: object
16341
+ properties:
16342
+ promptTokens:
16343
+ type: number
16344
+ outputTokens:
16345
+ type: number
16346
+ cachedTokens:
16347
+ type: number
16348
+ totalTokens:
16349
+ type: number
16350
+ requests:
16351
+ type: number
16352
+ images:
16353
+ type: number
16354
+ required:
16355
+ - promptTokens
16356
+ - outputTokens
16357
+ - cachedTokens
16358
+ - totalTokens
16359
+ - requests
16360
+ - images
16341
16361
  VoiceSessionRequest:
16342
16362
  type: object
16343
16363
  properties:
@@ -23052,6 +23072,8 @@ components:
23052
23072
  type: string
23053
23073
  hubCustomDomain:
23054
23074
  type: string
23075
+ siteHost:
23076
+ type: string
23055
23077
  shortId:
23056
23078
  type: string
23057
23079
  dark:
@@ -30539,6 +30561,19 @@ components:
30539
30561
  appId:
30540
30562
  type: object
30541
30563
  additionalProperties: true
30564
+ FunctionSiteUrlOptions:
30565
+ type: object
30566
+ properties:
30567
+ channel:
30568
+ type: string
30569
+ path:
30570
+ type: string
30571
+ query:
30572
+ type: object
30573
+ additionalProperties:
30574
+ type: string
30575
+ host:
30576
+ type: string
30542
30577
  AllocateSequenceInput:
30543
30578
  type: object
30544
30579
  properties:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "2.0.35",
3
+ "version": "2.0.37",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",