# Flowstack SDK — Full Reference (SDK v0.3.3)
This file contains the complete API reference, generated from the canonical doc registry. Each section documents one hook, component, utility, or endpoint. Note: the `flowstack-sdk` package and the REST API are versioned independently — the version above is the SDK; the REST & SSE API section below carries its own version.
---
# Built Apps
The generated-site pattern: provider, auth flow, and data operations for apps built on Flowstack.
## FlowstackProvider
*provider* — The root provider that wraps a built app and supplies backend config (baseUrl, tenantId, mode, appScope) to all Flowstack hooks.
```ts
import { FlowstackProvider } from 'flowstack-sdk'
```
Wrap your entire app in `FlowstackProvider` (Quick Start step 1). It supplies the backend connection config consumed by every Flowstack hook.
For **built apps** the critical field is `appScope` — it scopes all data to the app and the build pipeline fills `__SITE_ID__` with the hex `site_id`. Built apps do NOT use workspaces; the backend creates them automatically.
Generated sites always run in `mode: 'production'` — auth, agent chat, and data all connect to real infrastructure at `sage-api.flowstack.fun`. Do not use mock mode for deployed sites. No Privy app ID, `privyConfig.appId`, or wallet peer deps are needed for the broker auth flow.
```ts
{children}
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `config` | `FlowstackConfig` | required | Backend configuration object (see fields below). |
| `config.baseUrl` | `string` | required | API base URL, e.g. `'https://sage-api.flowstack.fun'`. |
| `config.tenantId` | `string` | — | Optional. For authenticated calls the backend derives the tenant from the brokered-login JWT and ignores any client-sent value, so built apps should **not** set it. Required **only** for `usePublicCollection` (anonymous access has no token to derive from); if missing there, that hook raises a clear error rather than using a default. |
| `config.mode` | `'production' | 'development' | 'mock'` | required | Connection mode. Generated/built sites must use `'production'` (real infrastructure at `sage-api.flowstack.fun`). `'mock'` and `'development'` are for local SDK development only — never deploy a built app with them. |
| `config.appScope` | `string` | required | Built apps only — hex site ID that scopes all data to this app. The build pipeline auto-fills `__SITE_ID__`. |
| `config.jwtSecret` | `string` | — | Casino platform config, e.g. `'flowstack-cdn-site'`. |
| `config.passwordSecret` | `string` | — | Casino platform config, e.g. `'flowstack-cdn-site'`. |
| `children` | `ReactNode` | required | The app tree that consumes Flowstack hooks. |
### Examples
**Minimal built-app wrap**
```tsx
import { FlowstackProvider, useFlowstack, BrokeredLoginButton } from 'flowstack-sdk';
function App() {
return (
);
}
```
**Casino platform wrap**
```tsx
import { FlowstackProvider } from 'flowstack-sdk';
const config = {
baseUrl: 'https://sage-api.flowstack.fun',
tenantId: 't_6fe54402be43',
mode: 'production',
jwtSecret: 'flowstack-cdn-site',
passwordSecret: 'flowstack-cdn-site',
};
export default function App({ children }) {
return (
{children}
);
}
```
> ⚠️ **Warning:** **`appScope` is required for built apps.** It scopes all data to this app; the build pipeline fills `__SITE_ID__` with the hex site_id.
> ℹ️ **Note:** Generated sites always run in production mode and connect to real infrastructure at `sage-api.flowstack.fun`. Do not use mock mode for deployed sites.
> ℹ️ **Note:** No `privyConfig.appId` or wallet peer deps (`@privy-io/react-auth`, `wagmi`, `viem`) are needed for the broker auth flow.
> ℹ️ **Note:** **`tenantId` is derived from the JWT** — don't set it for built apps. The backend reads the tenant from the brokered-login token and ignores any client-sent `X-Tenant-ID`. The only place it must be set explicitly is `usePublicCollection` (anonymous, no token).
**See also:** [useFlowstack](https://flowstack.fun/docs/useFlowstack) · [BrokeredLoginButton](https://flowstack.fun/docs/BrokeredLoginButton) · [AuthGuard](https://flowstack.fun/docs/AuthGuard) · [useCollection](https://flowstack.fun/docs/useCollection) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#built-apps--generated-site-pattern*
## useFlowstack
*hook* — The hook built apps use to read auth state — isAuthenticated, isInitialized, and credentials — instead of useAuth().
```ts
import { useFlowstack } from 'flowstack-sdk'
```
For built apps, read auth state from `useFlowstack()` instead of `useAuth()`. It exposes whether the provider has initialized, whether a session is active, and the active credentials (e.g. `credentials?.email`). It also exposes `setCredentials` — call `setCredentials(null)` to sign out.
Built apps authenticate via `BrokeredLoginButton`; `useFlowstack()` is how they observe the resulting auth state and identity.
```ts
const { isAuthenticated, isInitialized, credentials, setCredentials } = useFlowstack()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `isAuthenticated` | `boolean` | Whether a session is active. |
| `isInitialized` | `boolean` | Whether the provider has finished initializing — gate on this before rendering auth-dependent UI. |
| `credentials` | `FlowstackCredentials | null` | Active session credentials, e.g. `credentials?.email`. |
| `setCredentials` | `(creds: FlowstackCredentials | null) => void` | Set or clear credentials; call `setCredentials(null)` to sign out. |
### Examples
**Gate UI on auth state**
```tsx
function AuthGate() {
const { isAuthenticated, isInitialized } = useFlowstack();
if (!isInitialized) return
Loading...
;
// BrokeredLoginButton opens Casino's auth popup — Privy runs there, not here
return isAuthenticated ? : (
);
}
```
**Read identity and sign out**
```tsx
function Header() {
const { credentials, setCredentials } = useFlowstack();
return (
{credentials?.email}
);
}
```
> ℹ️ **Note:** Built apps read auth state from `useFlowstack()`, not `useAuth()`. After auth, read identity from `credentials` here.
**See also:** [BrokeredLoginButton](https://flowstack.fun/docs/BrokeredLoginButton) · [AuthGuard](https://flowstack.fun/docs/AuthGuard) · [useAuth](https://flowstack.fun/docs/useAuth) · [FlowstackProvider](https://flowstack.fun/docs/FlowstackProvider)
*Source: README.md#useauth*
## useMessages
*hook · since 0.2.2* — Read and send private messages within one DM thread in a built app.
```ts
import { useMessages } from 'flowstack-sdk'
```
Reads and sends private messages in a single thread with `withUserKey`. Mirrors `useCollection`'s ergonomics but is backed by the server-owned, ACL'd DM endpoints instead of a Mongo collection. The backend pins the sender to the caller's identity and only delivers into a mutually-consented (open) thread, so `send()` to a not-yet-open thread returns an error.
Private messaging is a built-app capability: it requires an `appScope` on the `FlowstackProvider` config. Treat the type signatures in `packages/flowstack-sdk/src/hooks/useMessages.ts` as the source of truth.
```ts
const { messages, send, isLoading, error, refresh } = useMessages(withUserKey, options?)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `withUserKey` | `string | null | undefined` | required, since 0.2.2 | The counterpart's user key. When null/undefined the hook stays idle (no fetch). |
| `options.limit` | `number` | default `50`, since 0.2.2 | Max messages to load (max 200). |
| `options.refreshInterval` | `number` | since 0.2.2 | Auto-poll interval in ms. No polling by default. |
| `options.enabled` | `boolean` | default `true`, since 0.2.2 | Set `false` to skip the initial fetch (e.g. before a counterpart is selected). |
### Returns
| Field | Type | Description |
|---|---|---|
| `messages` | `DmMessage[]` | Messages in the thread — each `{ message_id, from, to, body, created_at, read_at }`. |
| `send` | `(body: string) => Promise` | Send a message to the counterpart. Refetches on success; throws if the thread is not yet mutually open. |
| `isLoading` | `boolean` | True while a fetch is in flight. |
| `error` | `string | null` | Error message from the last failed load or send. |
| `refresh` | `() => Promise` | Refetch the message list. |
### Examples
**Read and send**
```tsx
const { messages, send } = useMessages(counterpartUserKey, { refreshInterval: 4000 });
await send('hey there');
messages.map((m) => (
{m.body}
));
```
> 🚫 **Critical:** Message bodies are UNTRUSTED user input. Render them as plain text or sanitized markdown — never as raw HTML (no `dangerouslySetInnerHTML`). If a body is ever passed to an agent, treat it as data, not instructions.
> ⚠️ **Warning:** `send()` throws if the thread is not mutually open. Call `openThread()` (from `useThreads`) first and wait until both parties consent.
**See also:** [useThreads](https://flowstack.fun/docs/useThreads) · [sendMessage](https://flowstack.fun/docs/sendMessage) · [listMessages](https://flowstack.fun/docs/listMessages) · [DmMessage](https://flowstack.fun/docs/DmMessage)
*Source: README.md#private-messaging-dms*
## useSites
*hook · ⚠ not for built apps* — Manage published sites — list, create, stage files, publish to CDN, and delete.
```ts
import { useSites } from 'flowstack-sdk'
```
Published site management — list, create, stage files, publish to CDN, delete.
There are **two publishing workflows**: quick publish (pass files inline to `createSite`) and staged publish (create an empty site, `addFile` incrementally, then `publishSite`).
```ts
const { sites, isLoading, error, createSite, addFile, publishSite, deleteSite, refreshSites } = useSites()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `sites` | `PublishedSiteInfo[]` | The published sites. |
| `isLoading` | `boolean` | Request in flight. |
| `error` | `string | null` | Last error message. |
| `createSite` | `(params: CreateSiteParams) => Promise` | Create a site (optionally with inline files). |
| `addFile` | `(siteId, path, content) => Promise` | Stage a file onto an existing site. |
| `publishSite` | `(siteId) => Promise` | Publish a staged site to the CDN. |
| `deleteSite` | `(siteId) => Promise` | Delete a site. |
| `refreshSites` | `() => Promise` | Re-fetch the site list. |
### Examples
**Hook surface**
```tsx
const {
sites, // PublishedSiteInfo[]
isLoading, // boolean
error, // string | null
createSite, // (params: CreateSiteParams) => Promise
addFile, // (siteId, path, content) => Promise
publishSite, // (siteId) => Promise
deleteSite, // (siteId) => Promise
refreshSites, // () => Promise
} = useSites();
```
**Two publishing workflows**
```tsx
// Quick publish — pass files inline
await createSite({
name: 'My Dashboard',
siteType: 'on_demand',
files: { 'index.html': '...', 'styles.css': 'body { ... }' },
});
// Staged publish — add files incrementally, then publish
const site = await createSite({ name: 'My App' });
await addFile(site.id, 'index.html', '...');
await addFile(site.id, 'src/App.tsx', 'export default function App() { ... }');
await publishSite(site.id);
```
> ℹ️ **Note:** **PublishedSiteInfo fields:** `id` (string) site ID; `name` (string) display name; `url` (string) published CDN URL; `shortUrl` (string?) short URL for sharing; `siteType` (`'on_demand' | 'daily' | 'js_build'`) how the site was created; `fileCount` (number) number of files; `totalBytes` (number?) total size; `createdAt` (string) ISO timestamp; `expiresAt` (string?) expiration if applicable.
*Source: README.md#usesites*
## useThreads
*hook · since 0.2.2* — List a built-app user's private message threads, with unread counts and consent control.
```ts
import { useThreads } from 'flowstack-sdk'
```
Lists the authenticated user's private DM threads from the server-owned, ACL'd DM store — the backend only ever returns threads the caller participates in. Each thread carries the counterpart's user key, the last message, and an unread count.
Private messaging is a built-app capability: it requires an `appScope` on the `FlowstackProvider` config. In a Casino personal session the backend returns 403 and this hook surfaces an error. Treat the type signatures in `packages/flowstack-sdk/src/hooks/useThreads.ts` as the source of truth.
```ts
const { threads, isLoading, error, refresh, openThread } = useThreads(options?)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `options.refreshInterval` | `number` | since 0.2.2 | Auto-poll interval in ms. No polling by default. |
| `options.enabled` | `boolean` | default `true`, since 0.2.2 | Set `false` to skip the initial fetch. |
### Returns
| Field | Type | Description |
|---|---|---|
| `threads` | `DmThread[]` | The caller's threads — each `{ pair_key, with_user_key, status, last_message, unread_count, updated_at }`. |
| `isLoading` | `boolean` | True while a fetch is in flight. |
| `error` | `string | null` | Error message, e.g. a 403 when used outside a built-app (no `appScope`). |
| `refresh` | `() => Promise` | Refetch the thread list. |
| `openThread` | `(withUserKey: string) => Promise<'pending' | 'open' | null>` | Record the caller's consent to open a thread. The thread becomes sendable only once BOTH parties consent. Idempotent per caller; refetches on success. |
### Examples
**List threads with polling**
```tsx
const { threads, isLoading, openThread } = useThreads({ refreshInterval: 5000 });
threads.map((t) => (
));
// Consent to open a new conversation
const status = await openThread(otherUserKey); // 'pending' | 'open' | null
```
> ⚠️ **Warning:** Requires a built-app `appScope`. Outside a built app the backend returns 403 and `error` is set.
**See also:** [useMessages](https://flowstack.fun/docs/useMessages) · [listThreads](https://flowstack.fun/docs/listThreads) · [openThread](https://flowstack.fun/docs/openThread) · [DmThread](https://flowstack.fun/docs/DmThread)
*Source: README.md#private-messaging-dms*
## activateDomain
*utility · since 0.2.8 · ⚠ not for built apps* — Client function: activate a domain once its ACM cert is issued (CloudFront goes live ~5–15 min after).
```ts
import { activateDomain } from 'flowstack-sdk'
```
Non-React client function. Call after `getDomainStatus` reports `status: "issued"`. `POST /api/v1/apps/{siteId}/domains/{domain}/activate`.
```ts
activateDomain(credentials, siteId, domain, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `domain` | `string` | required | The custom domain, e.g. `app.example.com`. |
| `config` | `FlowstackClientConfig` | since 0.2.8 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `ActivateDomainResult`: `{ domain, site_id, status: "live", url, message }`. |
### Examples
**Activate**
```ts
const res = await activateDomain(credentials, siteId, 'app.example.com', { appScope });
if (res.ok) console.log('live at', res.data.url);
```
> ℹ️ **Note:** The cert must be `issued` first (see `getDomainStatus`). After activation CloudFront propagation takes ~5–15 minutes before the domain serves.
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [getDomainStatus](https://flowstack.fun/docs/getDomainStatus) · [connectDomain](https://flowstack.fun/docs/connectDomain)
*Source: src/api/client.ts (Custom Domains, P0-158)*
## clearStagedFiles
*utility · since 0.2.7 · ⚠ not for built apps* — Client function: clear all staged files for a site (the .staging marker is preserved).
```ts
import { clearStagedFiles } from 'flowstack-sdk'
```
Non-React client function. `DELETE /api/v1/sites/{siteId}/staged`.
```ts
clearStagedFiles(credentials, siteId, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `config` | `FlowstackClientConfig` | since 0.2.7 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | How many staged files were deleted. |
### Examples
**Clear staged files**
```ts
await clearStagedFiles(credentials, siteId, { appScope });
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [stageFiles](https://flowstack.fun/docs/stageFiles) · [listStagedFiles](https://flowstack.fun/docs/listStagedFiles)
*Source: src/api/client.ts (Site Ops, P0-156)*
## connectDomain
*utility · since 0.2.8 · ⚠ not for built apps* — Client function: start connecting a custom domain — requests an ACM cert and returns the DNS records to add.
```ts
import { connectDomain } from 'flowstack-sdk'
```
Non-React client function. Kicks off custom-domain setup: requests the ACM certificate (DNS validation) and returns the validation + routing records to add at the registrar. Then poll `getDomainStatus`, and call `activateDomain` once the cert is issued. `POST /api/v1/apps/{siteId}/domains` with `{ domain }`.
```ts
connectDomain(credentials, siteId, domain, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `domain` | `string` | required | The custom domain, e.g. `app.example.com`. |
| `config` | `FlowstackClientConfig` | since 0.2.8 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `ConnectDomainResult`: `{ domain, site_id, status, cert_arn, validation_records, routing_record, message }`. Add `validation_records` (and `routing_record`) at your DNS provider. |
### Examples
**Connect a domain**
```ts
const res = await connectDomain(credentials, siteId, 'app.example.com', { appScope });
if (res.ok) {
for (const r of res.data.validation_records) console.log(`${r.type} ${r.name} -> ${r.value}`);
console.log('routing:', res.data.routing_record);
}
```
> ℹ️ **Note:** Flow: `connectDomain` → add the returned DNS records → poll `getDomainStatus` until `status: "issued"` → `activateDomain`. CloudFront goes live ~5–15 min after activation.
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [getDomainStatus](https://flowstack.fun/docs/getDomainStatus) · [activateDomain](https://flowstack.fun/docs/activateDomain) · [listDomains](https://flowstack.fun/docs/listDomains) · [disconnectDomain](https://flowstack.fun/docs/disconnectDomain) · [CustomDomainRecord](https://flowstack.fun/docs/CustomDomainRecord)
*Source: src/api/client.ts (Custom Domains, P0-158)*
## disconnectDomain
*utility · since 0.2.8 · ⚠ not for built apps* — Client function: disconnect a custom domain from a site.
```ts
import { disconnectDomain } from 'flowstack-sdk'
```
Non-React client function. Removes the domain mapping. `DELETE /api/v1/apps/{siteId}/domains/{domain}`.
```ts
disconnectDomain(credentials, siteId, domain, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `domain` | `string` | required | The custom domain, e.g. `app.example.com`. |
| `config` | `FlowstackClientConfig` | since 0.2.8 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | Confirmation of removal. |
### Examples
**Disconnect**
```ts
await disconnectDomain(credentials, siteId, 'app.example.com', { appScope });
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [connectDomain](https://flowstack.fun/docs/connectDomain) · [listDomains](https://flowstack.fun/docs/listDomains)
*Source: src/api/client.ts (Custom Domains, P0-158)*
## dmPairKey
*utility · since 0.2.2* — Pure helper: deterministic, order-independent thread key for a pair of user keys.
```ts
import { dmPairKey } from 'flowstack-sdk'
```
Computes the deterministic `pair_key` for two user keys by sorting them and joining with `::`. Mirrors the backend's thread-key derivation, so `dmPairKey(a, b) === dmPairKey(b, a)`. Pure — no credentials, no network. Useful for locally matching a thread to a counterpart.
```ts
dmPairKey(a: string, b: string): string
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `a` | `string` | required, since 0.2.2 | One user key. |
| `b` | `string` | required, since 0.2.2 | The other user key. |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `string` | The sorted, `::`-joined pair key (order-independent). |
### Examples
**Derive a thread key**
```ts
dmPairKey('userA', 'userB'); // 'userA::userB'
dmPairKey('userB', 'userA'); // 'userA::userB' (same)
```
**See also:** [openThread](https://flowstack.fun/docs/openThread) · [useThreads](https://flowstack.fun/docs/useThreads) · [DmThread](https://flowstack.fun/docs/DmThread)
*Source: README.md#private-messaging-dms*
## getAppSource
*utility · since 0.2.7 · ⚠ not for built apps* — Client function: fetch a version's source files as a { path: content } map (live version by default).
```ts
import { getAppSource } from 'flowstack-sdk'
```
Non-React client function. Returns the source tree for a site version. Omit `version` for the live version. `GET /api/v1/sites/{siteId}/source?version={n}`.
```ts
getAppSource(credentials, siteId, version?, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `version` | `number` | — | Specific version to fetch. Defaults to the live version. |
| `config` | `FlowstackClientConfig` | since 0.2.7 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `AppSource`: `{ site_id, version, files: Record }` (version is null for legacy unversioned sites). |
### Examples
**Read the live source**
```ts
const res = await getAppSource(credentials, siteId, undefined, { appScope });
if (res.ok) for (const [p, content] of Object.entries(res.data.files)) console.log(p, content.length);
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [stageFiles](https://flowstack.fun/docs/stageFiles) · [useSiteVersions](https://flowstack.fun/docs/useSiteVersions)
*Source: src/api/client.ts (Site Ops, P0-156)*
## getDataPlan
*utility · since 0.2.7 · ⚠ not for built apps* — Client function: get the saved mongoCollections schema (data plan) for a site.
```ts
import { getDataPlan } from 'flowstack-sdk'
```
Non-React client function. `GET /api/v1/sites/{siteId}/data-plan`.
```ts
getDataPlan(credentials, siteId, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `config` | `FlowstackClientConfig` | since 0.2.7 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | The stored `DataPlan` (its `mongoCollections` schema). |
### Examples
**Read the data plan**
```ts
const res = await getDataPlan(credentials, siteId, { appScope });
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [setDataPlan](https://flowstack.fun/docs/setDataPlan)
*Source: src/api/client.ts (Site Ops, P0-156)*
## getDomainStatus
*utility · since 0.2.8 · ⚠ not for built apps* — Client function: poll a custom domain's validation/activation status.
```ts
import { getDomainStatus } from 'flowstack-sdk'
```
Non-React client function. Poll this after `connectDomain` until `status` is `issued`, then call `activateDomain`. `GET /api/v1/apps/{siteId}/domains/{domain}/status`.
```ts
getDomainStatus(credentials, siteId, domain, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `domain` | `string` | required | The custom domain, e.g. `app.example.com`. |
| `config` | `FlowstackClientConfig` | since 0.2.8 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `DomainStatusResult`: `{ domain, site_id, status, cert_arn?, validation_records, routing_record, connected_at?, last_check? }`. `status` is one of `validating | issued | activating | live | failed`. |
### Examples
**Poll until issued**
```ts
const res = await getDomainStatus(credentials, siteId, 'app.example.com', { appScope });
if (res.ok && res.data.status === 'issued') {
await activateDomain(credentials, siteId, 'app.example.com', { appScope });
}
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [connectDomain](https://flowstack.fun/docs/connectDomain) · [activateDomain](https://flowstack.fun/docs/activateDomain) · [CustomDomainRecord](https://flowstack.fun/docs/CustomDomainRecord)
*Source: src/api/client.ts (Custom Domains, P0-158)*
## listDomains
*utility · since 0.2.8 · ⚠ not for built apps* — Client function: list the custom domains connected to a site and their status.
```ts
import { listDomains } from 'flowstack-sdk'
```
Non-React client function. `GET /api/v1/apps/{siteId}/domains`.
```ts
listDomains(credentials, siteId, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `config` | `FlowstackClientConfig` | since 0.2.8 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | All connected domains as `CustomDomainRecord[]`. |
### Examples
**List domains**
```ts
const res = await listDomains(credentials, siteId, { appScope });
if (res.ok) res.data.domains.forEach(d => console.log(d.domain, d.status));
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [connectDomain](https://flowstack.fun/docs/connectDomain) · [getDomainStatus](https://flowstack.fun/docs/getDomainStatus) · [CustomDomainRecord](https://flowstack.fun/docs/CustomDomainRecord)
*Source: src/api/client.ts (Custom Domains, P0-158)*
## listMessages
*utility · since 0.2.2* — Client function: list messages in the caller's thread with a counterpart. ACL'd server-side.
```ts
import { listMessages } from 'flowstack-sdk'
```
Non-React client function backing `useMessages`. Lists messages in the caller's thread with `withUserKey`. Reads are ACL'd to thread participants. `config` must carry an `appScope`.
```ts
listMessages(credentials, withUserKey, options?, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required, since 0.2.2 | The authenticated caller's credentials. |
| `withUserKey` | `string` | required, since 0.2.2 | The counterpart's user key. |
| `options.limit` | `number` | default `50`, since 0.2.2 | Max messages to load (max 200). |
| `options.before` | `string` | since 0.2.2 | Cursor: return messages created before this id/timestamp (pagination). |
| `config` | `FlowstackClientConfig` | required, since 0.2.2 | Client config. Must include `appScope`. |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `{ ok, data: { messages, count }, error }`. |
### Examples
**List messages**
```ts
const res = await listMessages(credentials, counterpartKey, { limit: 50 }, { appScope });
if (res.ok) console.log(res.data.messages);
```
> 🚫 **Critical:** `body` on each `DmMessage` is UNTRUSTED user input. Render as text / sanitized markdown, never raw HTML.
**See also:** [useMessages](https://flowstack.fun/docs/useMessages) · [sendMessage](https://flowstack.fun/docs/sendMessage) · [DmMessage](https://flowstack.fun/docs/DmMessage)
*Source: README.md#private-messaging-dms*
## listStagedFiles
*utility · since 0.2.7 · ⚠ not for built apps* — Client function: list the files currently staged for a site.
```ts
import { listStagedFiles } from 'flowstack-sdk'
```
Non-React client function. `GET /api/v1/sites/{siteId}/staged`.
```ts
listStagedFiles(credentials, siteId, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `config` | `FlowstackClientConfig` | since 0.2.7 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `StagedFilesList`: `{ site_id, staged_files: { path, size_bytes, staged_at }[], count }`. |
### Examples
**List staged files**
```ts
const res = await listStagedFiles(credentials, siteId, { appScope });
if (res.ok) console.log(res.data.count, 'staged');
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [stageFiles](https://flowstack.fun/docs/stageFiles) · [clearStagedFiles](https://flowstack.fun/docs/clearStagedFiles)
*Source: src/api/client.ts (Site Ops, P0-156)*
## listThreads
*utility · since 0.2.2* — Client function: list the caller's DM threads (counterpart key, last message, unread count).
```ts
import { listThreads } from 'flowstack-sdk'
```
Non-React client function backing `useThreads`. Lists the caller's threads from the server-owned, ACL'd DM store. `config` must carry an `appScope` (built-app context) or the call throws before any request.
```ts
listThreads(credentials, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required, since 0.2.2 | The authenticated caller's credentials. |
| `config` | `FlowstackClientConfig` | required, since 0.2.2 | Client config. Must include `appScope`; `baseUrl`/`tenantId` as needed. |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `{ ok, data: { threads, count }, error }`. ACL'd server-side to the caller's threads. |
### Examples
**List threads**
```ts
const res = await listThreads(credentials, { appScope });
if (res.ok) console.log(res.data.threads);
```
> ⚠️ **Warning:** Throws `Private messaging requires an app scope` if `config.appScope` is missing.
**See also:** [useThreads](https://flowstack.fun/docs/useThreads) · [listMessages](https://flowstack.fun/docs/listMessages) · [DmThread](https://flowstack.fun/docs/DmThread)
*Source: README.md#private-messaging-dms*
## markMessageRead
*utility · since 0.2.2* — Client function: mark a received DM as read (recipient only).
```ts
import { markMessageRead } from 'flowstack-sdk'
```
Non-React client function. Marks the message `messageId` as read. Only the recipient may mark a message read; the backend rejects others. Sets `read_at` on the message and decrements the thread's `unread_count`. `config` must carry an `appScope`.
```ts
markMessageRead(credentials, messageId, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required, since 0.2.2 | The recipient's credentials. |
| `messageId` | `string` | required, since 0.2.2 | The `message_id` of the received message to mark read. |
| `config` | `FlowstackClientConfig` | required, since 0.2.2 | Client config. Must include `appScope`. |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `{ ok, data: { message_id, read }, error }`. |
### Examples
**Mark read**
```ts
await markMessageRead(credentials, msg.message_id, { appScope });
```
> ℹ️ **Note:** Recipient-only. The sender cannot mark their own outgoing message read.
**See also:** [useMessages](https://flowstack.fun/docs/useMessages) · [listMessages](https://flowstack.fun/docs/listMessages) · [DmMessage](https://flowstack.fun/docs/DmMessage)
*Source: README.md#private-messaging-dms*
## openThread
*utility · since 0.2.2* — Client function: record the caller's consent to open a DM thread with another user.
```ts
import { openThread } from 'flowstack-sdk'
```
Non-React client function backing `useThreads().openThread`. Records the caller's consent to open a thread with `withUserKey`. The thread becomes `open` (sendable) only once BOTH parties have consented; until then it is `pending`. Idempotent per caller. `config` must carry an `appScope`, and the caller's credentials must include a `userId`.
```ts
openThread(credentials, withUserKey, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required, since 0.2.2 | The authenticated caller's credentials (must include `userId`). |
| `withUserKey` | `string` | required, since 0.2.2 | The counterpart's user key. |
| `config` | `FlowstackClientConfig` | required, since 0.2.2 | Client config. Must include `appScope`. |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `{ ok, data: { pair_key, status: 'pending' | 'open' }, error }`. |
### Examples
**Open a thread**
```ts
const res = await openThread(credentials, otherUserKey, { appScope });
if (res.ok) console.log(res.data.status); // 'pending' until the other party also consents
```
> ℹ️ **Note:** The thread key is derived as `dmPairKey(me, withUserKey)` — deterministic and order-independent.
**See also:** [useThreads](https://flowstack.fun/docs/useThreads) · [sendMessage](https://flowstack.fun/docs/sendMessage) · [dmPairKey](https://flowstack.fun/docs/dmPairKey)
*Source: README.md#private-messaging-dms*
## purchaseDomain
*utility · since 0.3.1 · ⚠ not for built apps* — Client function: buy a domain through Casino — returns a Stripe Checkout URL; registration + site auto-connect fire from the payment webhook (P0-163).
```ts
import { purchaseDomain } from 'flowstack-sdk'
```
Non-React client function. Starts a domain purchase through Casino's registrar: `POST /api/v1/registrar/purchase` with a `PurchaseDomainRequest` returns a Stripe Checkout `session_url` — redirect the builder there to pay. Route 53 registration and (if `site_id` was set) automatic `connectDomain`-style wiring happen asynchronously from the payment webhook, NOT in this call. Use `searchDomains` first to confirm availability and price.
```ts
purchaseDomain(credentials, request, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The builder's credentials (owner-scoped JWT). |
| `request` | `PurchaseDomainRequest` | required | Domain (name without TLD), TLD, `RegistrantContact`, and options (`auto_renew`, `whois_privacy`, `site_id` for auto-connect). |
| `config` | `FlowstackClientConfig` | — | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | The Stripe Checkout URL to redirect the builder to, plus the quoted first-year price. |
### Examples
**Buy a domain and auto-connect it to a site**
```ts
const res = await purchaseDomain(credentials, {
domain: 'thezengarden',
tld: 'com',
registrant: {
first_name: 'Ada', last_name: 'Lovelace', email: 'ada@example.com',
phone: '+12125551234', address_line1: '1 Main St', city: 'New York',
state: 'NY', zip_code: '10001', country_code: 'US',
},
auto_renew: true,
whois_privacy: true,
site_id: siteId,
});
if (res.data?.session_url) window.location.href = res.data.session_url;
```
> ⚠️ **Warning:** The domain is NOT owned when this call returns — payment happens in the browser at `session_url`, and Route 53 registration + site auto-connect fire from the payment webhook afterwards. Poll `listDomains` / `getDomainStatus` to observe completion; do not treat a 200 here as a completed purchase.
> ⚠️ **Warning:** Owner/management operation — call it with the **builder's** credentials from a dashboard/builder context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
> ℹ️ **Note:** Registrant contact fields are registrar-validated: `phone` must be E.164 (`+12125551234`), `country_code` a 2-char ISO code, `state` a 2-char code for US addresses. Invalid contact data fails the registration after payment — get these right up front.
**See also:** [searchDomains](https://flowstack.fun/docs/searchDomains) · [PurchaseDomainRequest](https://flowstack.fun/docs/PurchaseDomainRequest) · [RegistrantContact](https://flowstack.fun/docs/RegistrantContact) · [listDomains](https://flowstack.fun/docs/listDomains) · [getDomainStatus](https://flowstack.fun/docs/getDomainStatus)
*Source: src/api/client.ts (Domain Registrar, P0-163)*
## renameSite
*utility · since 0.2.7 · ⚠ not for built apps* — Client function: rename a site (display name in the manifest + dashboard metadata).
```ts
import { renameSite } from 'flowstack-sdk'
```
Non-React client function. Updates a built app's display name. `PATCH /api/v1/sites/{siteId}` with `{ name }`.
```ts
renameSite(credentials, siteId, name, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `name` | `string` | required | The new display name. |
| `config` | `FlowstackClientConfig` | since 0.2.7 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `{ ok, data: { site_id, name }, error }`. |
### Examples
**Rename a site**
```ts
const res = await renameSite(credentials, siteId, 'My CRM', { appScope });
if (!res.ok) console.error(res.error);
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [getAppSource](https://flowstack.fun/docs/getAppSource) · [useSiteVersions](https://flowstack.fun/docs/useSiteVersions)
*Source: src/api/client.ts (Site Ops, P0-156)*
## searchDomains
*utility · since 0.3.1 · ⚠ not for built apps* — Client function: check domain availability + Casino retail pricing across TLDs (P0-163).
```ts
import { searchDomains } from 'flowstack-sdk'
```
Non-React client function. Checks availability and Casino retail pricing (markup included) for a name across one or more TLDs. `GET /api/v1/registrar/check?name={name}&tlds={tlds}`. This is the search half of the registrar buy plane — pair it with `purchaseDomain` to complete a purchase, or with `connectDomain` if the user already owns the domain elsewhere.
```ts
searchDomains(credentials, name, tlds?, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The builder's credentials (owner-scoped JWT). |
| `name` | `string` | required | The name to check, without TLD — e.g. `thezengarden`. |
| `tlds` | `string` | — | Comma-separated TLDs to check, e.g. `com,dev,app`. Omit for the default set. |
| `config` | `FlowstackClientConfig` | — | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | One `DomainSearchResult` per TLD checked. `degraded: true` means every lookup failed (registrar outage). |
### Examples
**Check a name across TLDs**
```ts
const res = await searchDomains(credentials, 'thezengarden', 'com,dev,app');
if (res.data?.degraded) {
showError('Domain search is temporarily unavailable — try again shortly.');
} else {
for (const r of res.data.results) {
if (r.available) console.log(`${r.domain} — $${r.price_usd}/yr`);
}
}
```
> ⚠️ **Warning:** `degraded: true` means every lookup failed (registrar outage) — show an error state, NOT "taken". Per-TLD, `available: null` means that single lookup failed; only `available: false` means the domain is actually taken.
> ⚠️ **Warning:** Owner/management operation — call it with the **builder's** credentials from a dashboard/builder context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [purchaseDomain](https://flowstack.fun/docs/purchaseDomain) · [DomainSearchResult](https://flowstack.fun/docs/DomainSearchResult) · [connectDomain](https://flowstack.fun/docs/connectDomain) · [listDomains](https://flowstack.fun/docs/listDomains)
*Source: src/api/client.ts (Domain Registrar, P0-163)*
## sendMessage
*utility · since 0.2.2* — Client function: send a private message. 403 unless the thread is mutually open.
```ts
import { sendMessage } from 'flowstack-sdk'
```
Non-React client function backing `useMessages().send`. Sends `body` to `toUserKey`. The backend pins `from` to the caller's JWT identity and returns 403 unless the thread is mutually consented (open). `config` must carry an `appScope`.
```ts
sendMessage(credentials, toUserKey, body, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required, since 0.2.2 | The authenticated sender's credentials. |
| `toUserKey` | `string` | required, since 0.2.2 | The recipient's user key. |
| `body` | `string` | required, since 0.2.2 | Message text. Stored and returned verbatim as untrusted input. |
| `config` | `FlowstackClientConfig` | required, since 0.2.2 | Client config. Must include `appScope`. |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `{ ok, data: { message_id, created_at }, error }`. `ok` is false (403) if the thread is not mutually open. |
### Examples
**Send a message**
```ts
const res = await sendMessage(credentials, toUserKey, 'hey there', { appScope });
if (!res.ok) console.error(res.error); // e.g. thread not open
```
> ⚠️ **Warning:** Returns 403 unless BOTH parties have consented via `openThread`. There is no way to message a user who has not opened a thread with you.
**See also:** [useMessages](https://flowstack.fun/docs/useMessages) · [openThread](https://flowstack.fun/docs/openThread) · [listMessages](https://flowstack.fun/docs/listMessages)
*Source: README.md#private-messaging-dms*
## setDataPlan
*utility · since 0.2.7 · ⚠ not for built apps* — Client function: merge collection schemas into the site's data plan (PUT-with-merge).
```ts
import { setDataPlan } from 'flowstack-sdk'
```
Non-React client function. Merges collection schemas: new names override, other existing names are preserved. `PUT /api/v1/sites/{siteId}/data-plan` with `{ collections }`.
```ts
setDataPlan(credentials, siteId, collections, config?): Promise }>>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `collections` | `Record` | required | Collection-name → schema map to merge into the plan. |
| `config` | `FlowstackClientConfig` | since 0.2.7 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise }>>` | The saved collection names and the resulting merged `mongoCollections`. |
### Examples
**Merge a collection schema**
```ts
await setDataPlan(credentials, siteId, {
tasks: { fields: { title: 'string', done: 'boolean' } },
}, { appScope });
```
> ℹ️ **Note:** Merge semantics: a name present in `collections` overrides that collection; names you omit are left untouched. It never wipes the whole plan.
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [getDataPlan](https://flowstack.fun/docs/getDataPlan)
*Source: src/api/client.ts (Site Ops, P0-156)*
## stageFiles
*utility · since 0.2.7 · ⚠ not for built apps* — Client function: stage multiple files in one call (batch form of addSiteFile).
```ts
import { stageFiles } from 'flowstack-sdk'
```
Non-React client function. Stages a `{ path: content }` map for a site ahead of a build/publish. `POST /api/v1/sites/{siteId}/staged` with `{ files }`.
```ts
stageFiles(credentials, siteId, files, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `files` | `Record` | required | Map of file path → file content to stage. |
| `config` | `FlowstackClientConfig` | since 0.2.7 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | Count, paths, and total bytes of what was staged. |
### Examples
**Stage two files**
```ts
const res = await stageFiles(credentials, siteId, {
'index.html': '
hi
',
'app.js': 'console.log(1)',
}, { appScope });
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [listStagedFiles](https://flowstack.fun/docs/listStagedFiles) · [clearStagedFiles](https://flowstack.fun/docs/clearStagedFiles) · [getAppSource](https://flowstack.fun/docs/getAppSource)
*Source: src/api/client.ts (Site Ops, P0-156)*
## CustomDomainRecord
*type · since 0.2.8 · ⚠ not for built apps* — Type: a connected custom domain and its lifecycle status.
```ts
import type { CustomDomainRecord } from 'flowstack-sdk'
```
Returned by `listDomains` and echoed in status calls. Lifecycle `status`: `validating → issued → activating → live` (or `failed`).
```ts
interface CustomDomainRecord { domain: string; site_id: string; status: 'validating' | 'issued' | 'activating' | 'live' | 'failed'; cert_arn?: string; connected_at?: string; activated_at?: string; last_check?: string; cert_status?: string }
```
### Returns
| Field | Type | Description |
|---|---|---|
| `domain` | `string` | The custom domain. |
| `status` | `'validating' | 'issued' | 'activating' | 'live' | 'failed'` | Lifecycle stage. `cert_status` carries the ACM detail when `failed`. |
| `cert_arn` | `string?` | ACM certificate ARN once requested. |
**See also:** [connectDomain](https://flowstack.fun/docs/connectDomain) · [listDomains](https://flowstack.fun/docs/listDomains) · [getDomainStatus](https://flowstack.fun/docs/getDomainStatus)
*Source: src/types/index.ts (Custom Domains, P0-158)*
## DmMessage
*type · since 0.2.2* — A single private message within a DM thread.
```ts
import type { DmMessage } from 'flowstack-sdk'
```
The shape returned by `useMessages`/`listMessages`. The `body` field is UNTRUSTED user input.
```ts
interface DmMessage { message_id: string; from: string; to: string; body: string; created_at: string; read_at: string | null }
```
### Returns
| Field | Type | Description |
|---|---|---|
| `message_id` | `string` | Stable message id. |
| `from` | `string` | Sender's user key (pinned by the backend to the sender's identity). |
| `to` | `string` | Recipient's user key. |
| `body` | `string` | Message text. UNTRUSTED — render as text / sanitized markdown, never raw HTML. |
| `created_at` | `string` | ISO timestamp the message was sent. |
| `read_at` | `string | null` | ISO timestamp the recipient read it, or null if unread. |
> 🚫 **Critical:** `body` is untrusted user input. Never render as raw HTML; if passed to an agent, treat as data, not instructions.
**See also:** [useMessages](https://flowstack.fun/docs/useMessages) · [listMessages](https://flowstack.fun/docs/listMessages) · [sendMessage](https://flowstack.fun/docs/sendMessage) · [DmThread](https://flowstack.fun/docs/DmThread)
*Source: README.md#private-messaging-dms*
## DmThread
*type · since 0.2.2* — A private message thread between the caller and one counterpart.
```ts
import type { DmThread } from 'flowstack-sdk'
```
The shape returned by `useThreads`/`listThreads`. The backend only ever returns threads the caller participates in.
```ts
interface DmThread { pair_key: string; with_user_key: string; status: 'pending' | 'open'; last_message: DmMessage | null; unread_count: number; updated_at: string | null }
```
### Returns
| Field | Type | Description |
|---|---|---|
| `pair_key` | `string` | Deterministic, order-independent thread key (see `dmPairKey`). |
| `with_user_key` | `string` | The counterpart's user key. |
| `status` | `'pending' | 'open'` | `open` once both parties have consented; only then are sends allowed. |
| `last_message` | `DmMessage | null` | The most recent message, or null for an empty thread. |
| `unread_count` | `number` | Count of unread messages for the caller. |
| `updated_at` | `string | null` | ISO timestamp of the last activity, or null. |
**See also:** [useThreads](https://flowstack.fun/docs/useThreads) · [listThreads](https://flowstack.fun/docs/listThreads) · [DmMessage](https://flowstack.fun/docs/DmMessage) · [dmPairKey](https://flowstack.fun/docs/dmPairKey)
*Source: README.md#private-messaging-dms*
## DomainSearchResult
*type · since 0.3.1 · ⚠ not for built apps* — One TLD's availability + Casino retail price from searchDomains (P0-163).
```ts
import type { DomainSearchResult } from 'flowstack-sdk'
```
A single row of a `searchDomains` response: whether `{name}.{tld}` is available and what Casino charges per year. `available` is tri-state — `null` means the lookup for this TLD failed (transient), which is NOT the same as taken.
```ts
interface DomainSearchResult
```
### Returns
| Field | Type | Description |
|---|---|---|
| `domain` | `string` | The full domain checked, e.g. `thezengarden.com`. |
| `tld` | `string` | The TLD for this row, e.g. `com`. |
| `available` | `boolean | null` | `true` = purchasable, `false` = taken, `null` = the lookup for this TLD failed — retry or show an unknown state, never "taken". |
| `price_usd` | `number | null` | Casino retail price per year (markup included); `null` when unknown. |
| `aws_cost_usd` | `number | null?` | Underlying Route 53 cost per year, when surfaced. |
> ⚠️ **Warning:** Treat `available: null` as "unknown", not "taken" — rendering failed lookups as taken silently loses sales during registrar blips.
**See also:** [searchDomains](https://flowstack.fun/docs/searchDomains) · [purchaseDomain](https://flowstack.fun/docs/purchaseDomain) · [PurchaseDomainRequest](https://flowstack.fun/docs/PurchaseDomainRequest)
*Source: src/types/index.ts (Domain Registrar, P0-163)*
## PurchaseDomainRequest
*type · since 0.3.1 · ⚠ not for built apps* — Request body for purchaseDomain: name + TLD + registrant contact + auto-renew/privacy/auto-connect options (P0-163).
```ts
import type { PurchaseDomainRequest } from 'flowstack-sdk'
```
What `purchaseDomain` sends to `POST /api/v1/registrar/purchase`. Setting `site_id` makes the purchased domain auto-connect to that site after the payment webhook completes registration — the buy-and-wire path; omit it to just own the domain.
```ts
interface PurchaseDomainRequest
```
### Returns
| Field | Type | Description |
|---|---|---|
| `domain` | `string` | Name WITHOUT the TLD, e.g. `thezengarden` (not `thezengarden.com`). |
| `tld` | `string` | The TLD to register, e.g. `com`. |
| `registrant` | `RegistrantContact` | WHOIS contact — registrar-validated formats (E.164 phone, ISO country code). |
| `auto_renew` | `boolean?` | Renew automatically each year. |
| `whois_privacy` | `boolean?` | Enable WHOIS privacy protection. |
| `site_id` | `string?` | If set, the domain auto-connects to this site after payment — no separate `connectDomain` call needed. |
> ℹ️ **Note:** `domain` is the bare name — passing `thezengarden.com` as `domain` with `tld: "com"` would attempt `thezengarden.com.com`.
**See also:** [purchaseDomain](https://flowstack.fun/docs/purchaseDomain) · [RegistrantContact](https://flowstack.fun/docs/RegistrantContact) · [DomainSearchResult](https://flowstack.fun/docs/DomainSearchResult) · [searchDomains](https://flowstack.fun/docs/searchDomains)
*Source: src/types/index.ts (Domain Registrar, P0-163)*
## RegistrantContact
*type · since 0.3.1 · ⚠ not for built apps* — WHOIS registrant contact for a domain purchase — registrar-validated formats (P0-163).
```ts
import type { RegistrantContact } from 'flowstack-sdk'
```
The registrant block inside `PurchaseDomainRequest`. These fields go to the registrar (Route 53) as the domain's WHOIS contact, so formats are strictly validated — bad data fails the registration step after payment.
```ts
interface RegistrantContact
```
### Returns
| Field | Type | Description |
|---|---|---|
| `first_name` | `string` | Registrant first name. |
| `last_name` | `string` | Registrant last name. |
| `email` | `string` | Contact email — receives registrar verification mail; must be real. |
| `phone` | `string` | E.164 format, e.g. `+12125551234`. |
| `address_line1` | `string` | Street address. |
| `city` | `string` | City. |
| `state` | `string` | 2-char code for US addresses (e.g. `NY`); full name otherwise. |
| `zip_code` | `string` | Postal code. |
| `country_code` | `string` | 2-char ISO 3166 code, e.g. `US`. |
> ⚠️ **Warning:** Validation failures here surface AFTER payment (registration fires from the webhook) — validate `phone` (E.164) and `country_code` (ISO 3166 alpha-2) client-side before calling `purchaseDomain`.
**See also:** [PurchaseDomainRequest](https://flowstack.fun/docs/PurchaseDomainRequest) · [purchaseDomain](https://flowstack.fun/docs/purchaseDomain) · [searchDomains](https://flowstack.fun/docs/searchDomains)
*Source: src/types/index.ts (Domain Registrar, P0-163)*
---
# Authentication
Login, sessions, brokered login, and route guards.
## useAuth
*hook · since 0.1.0 · ⚠ not for built apps* — Read and mutate Casino-platform auth state (login, register, Google sign-in, logout, token refresh).
```ts
import { useAuth } from 'flowstack-sdk'
```
`useAuth()` is for Casino platform internals. Built apps must NOT use it — they authenticate via `BrokeredLoginButton` and read auth state from `useFlowstack()` instead. Calling `login(email, password)` from a built app always fails because end-users don't have Casino credentials.
```ts
const { user, credentials, isAuthenticated, isLoading, error, login, register, googleSignIn, logout, refreshToken } = useAuth()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `user` | `User | null` | The authenticated user, or null. |
| `credentials` | `FlowstackCredentials | null` | Active session credentials. |
| `isAuthenticated` | `boolean` | Whether a session is active. |
| `isLoading` | `boolean` | Auth request in flight. |
| `error` | `string | null` | Last auth error message. |
| `login` | `(email: string, password: string) => Promise` | Casino platform only — password login. |
| `register` | `(email: string, password: string, name?: string) => Promise` | Casino platform only — create an account. |
| `googleSignIn` | `() => Promise` | Casino platform only — Google OAuth. |
| `logout` | `() => void` | Clear the session. |
| `refreshToken` | `() => Promise` | Refresh the access token. |
### Examples
**Built apps — read auth state instead**
```tsx
const { isAuthenticated, isInitialized, credentials } = useFlowstack();
```
> ⚠️ **Warning:** **Built apps: do not use this hook.** Read auth state from `useFlowstack()` (`isAuthenticated`, `isInitialized`, `credentials`) and authenticate via `BrokeredLoginButton`.
**See also:** [BrokeredLoginButton](https://flowstack.fun/docs/BrokeredLoginButton) · [useFlowstackStatus](https://flowstack.fun/docs/useFlowstackStatus) · [AuthGuard](https://flowstack.fun/docs/AuthGuard)
*Source: README.md#useauth*
## useAuthGuard
*hook* — Programmatic route protection that reports whether access is allowed and where to redirect.
```ts
import { useAuthGuard } from 'flowstack-sdk'
```
Programmatic route protection. Pass requirements (`requireAuth`, `requireWorkspace`, `redirectTo`) and read back whether the current user is allowed and, if not, where to send them.
```ts
const { isAllowed, isLoading, shouldRedirect, redirectTo } = useAuthGuard(options)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `options.requireAuth` | `boolean` | — | Require an authenticated session. |
| `options.requireWorkspace` | `boolean` | — | Require a selected workspace. |
| `options.redirectTo` | `string` | — | Path to redirect to when access is denied, e.g. `'/login'`. |
### Returns
| Field | Type | Description |
|---|---|---|
| `isAllowed` | `boolean` | Whether access is permitted. |
| `isLoading` | `boolean` | Guard check in flight. |
| `shouldRedirect` | `boolean` | Whether the caller should redirect. |
| `redirectTo` | `string | undefined` | Where to redirect when denied. |
### Examples
**Protect a route**
```tsx
const {
isAllowed, // boolean
isLoading, // boolean
shouldRedirect, // boolean
redirectTo, // string | undefined
} = useAuthGuard({
requireAuth: true,
requireWorkspace: true,
redirectTo: '/login',
});
```
**See also:** [useAuth](https://flowstack.fun/docs/useAuth) · [AuthGuard](https://flowstack.fun/docs/AuthGuard)
*Source: README.md#useauthguard*
## AuthGuard
*component* — Wrapper that renders children only when authenticated — showing a login fallback, or transparently issuing a guest session for opted-in built apps.
```ts
import { AuthGuard } from 'flowstack-sdk'
```
`AuthGuard` gates its children behind authentication. When the user is not signed in it renders the `fallback` — typically a login screen containing `BrokeredLoginButton`, which it can show automatically.
**Guest chat (built apps).** When the app has an `appScope` and the site enabled guest chat server-side (`app_config.allowGuestChat`), an unauthenticated visitor is transparently issued a short-lived guest session (`POST /auth/guest`) instead of seeing the login gate — removing the "sign up first" friction. Control is the per-site backend flag: if the site hasn't opted in, `/auth/guest` returns 403 and `AuthGuard` falls back to the normal login UI. It's a no-op unless the app is built with an `appScope` (the Casino dashboard has none, so it always requires real login). Opt out per-guard with `allowGuest={false}`.
**Never use `redirectTo`.** Use `fallback={}` instead — `redirectTo="/"` kicks users out before they can sign in.
```ts
}>{children}
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `fallback` | `React.ReactNode` | — | Rendered when the user is not authenticated — use a login screen with `BrokeredLoginButton`. |
| `requireWorkspace` | `boolean` | — | Require an active workspace, not just authentication. |
| `allowGuest` | `boolean` | default `true` | Opt-in guest chat for built apps. When the app has an `appScope` and the site enabled `allowGuestChat`, mints a guest session via `POST /auth/guest` instead of showing the login gate; falls back to login on 403. No-op without an `appScope`. Set `false` to always require login. |
| `children` | `React.ReactNode` | required | Protected content rendered only when authenticated. |
### Examples
**Guard with a login fallback**
```tsx
import { AuthGuard } from 'flowstack-sdk';
export default function App() {
return (
}>
);
}
// LoginScreen shows BrokeredLoginButton
function LoginScreen() {
return (
);
}
```
> 🚫 **Critical:** **Never use `redirectTo`.** Use `fallback={}` instead — `redirectTo="/"` kicks users out before they can sign in.
> ℹ️ **Note:** Guest chat (`allowGuest`, default true) is gated entirely by the site's server-side `allowGuestChat` flag and only applies to apps built with an `appScope`. It calls `POST /auth/guest`; a 403 means the site hasn't opted in and the login UI is shown instead.
**See also:** [BrokeredLoginButton](https://flowstack.fun/docs/BrokeredLoginButton) · [useFlowstack](https://flowstack.fun/docs/useFlowstack) · [useAuthGuard](https://flowstack.fun/docs/useAuthGuard) · [useAuth](https://flowstack.fun/docs/useAuth)
*Source: README.md#authguard--never-use-redirectto*
## BrokeredLoginButton
*component* — The only authentication method for built apps — a button that opens Casino's auth popup where Privy runs and posts a scoped JWT back to the app.
```ts
import { BrokeredLoginButton } from 'flowstack-sdk'
```
Built apps authenticate **only** via `BrokeredLoginButton` — not email/password, not Google sign-in, not the wallet module. The broker opens a Casino popup that handles Privy login on Casino's origin (the only origin registered with Privy). Privy creates an embedded wallet for the user, the popup closes, and it postMessages a scoped JWT back to your app.
No Privy app ID, no `privyConfig`, no wagmi/viem peer deps are needed here. After auth, read identity from `useFlowstack()` (`credentials`) — not `useAuth()`.
**Why not email/password?** End-users of built apps don't have Casino email/password accounts — they authenticate via Privy (embedded wallet + email/Google) through the broker, so `useAuth().login(email, password)` always returns an error for built-app end-users.
```ts
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `label` | `string` | — | Button text, e.g. `"Continue with Flowstack"`. |
### Examples
**Login page**
```tsx
import { useFlowstack, BrokeredLoginButton } from 'flowstack-sdk';
function LoginPage() {
// No email, no password, no Privy appId needed here
return (
Welcome
);
}
```
> ⚠️ **Warning:** Built apps must use `BrokeredLoginButton` — **not** `useAuth().login()`, **not** `LoginButton` from `flowstack-sdk/wallet`, and **not** `privyConfig.appId`. End-users don't have Casino email/password accounts.
> ℹ️ **Note:** After auth, read identity from `useFlowstack()` (`credentials`) — not `useAuth()`.
> ℹ️ **Note:** **Account switching:** after `logout()`, the next `BrokeredLoginButton` click passes `force_login=1`, so the broker purges its sticky Privy/Casino session and the user can sign in with a *different* account. Without this, brokered re-login silently returns the same identity.
**See also:** [useFlowstack](https://flowstack.fun/docs/useFlowstack) · [AuthGuard](https://flowstack.fun/docs/AuthGuard) · [useAuth](https://flowstack.fun/docs/useAuth) · [FlowstackProvider](https://flowstack.fun/docs/FlowstackProvider)
*Source: README.md#2-add-authentication*
---
# Data
MongoDB-backed collections, public submissions, datasets, and queries.
## Agent-Driven Data Models — Reason → Write → Read
*guide · since 0.2.1* — The core pattern for using an agent as a worker: the app reads context and passes it in the prompt, the agent reasons and writes a structured document, and the app reads the result back via useCollection.
This is the core pattern for using an agent as a worker, not just a chatbox — the proven way to get a data model that works on the first try (validated end-to-end by the Caveat app).
**The mental model:**
1. The **app reads** whatever context the agent needs (via `useCollection`) and hands it to the agent **inside the prompt**.
2. The **agent reasons**, then **writes its result as a document** to a MongoDB collection using its data tools (`insert_documents` / `update_documents`). The agent is the writer; its job is to persist a structured doc, not to "reply."
3. The **app reads the result back** via `useCollection` (reactively). **Never** parse the agent's chat text or `messages[]` for data — read the persisted document.
**MongoDB is schemaless — lean on it.** A collection imposes no fixed columns, so the agent can write **any shape**: nested objects, arrays of objects, ragged/optional fields, mixed types, per-row metadata. You do **not** need a migration, a fixed schema, or matching columns up front. Tell the agent the exact JSON shape you want in the prompt and it will write exactly that — the document *is* the schema, and it can evolve per write.
**Three steps in practice:**
1. **Read context in the app and pass it to the agent** (don't make the agent query for it). Reading a shared pool via `useCollection({ layer: 'shared' })` is reliable; hand the data to the agent as JSON in the prompt. Strip PII (email/phone) before handing rows to the agent.
2. **Build a deterministic prompt that names the EXACT document shape to write.** Keep prompt construction in a small builder function so it's testable and the shape is explicit. Tell the agent which collection to write and the precise JSON — it will follow it verbatim.
3. **Run the agent as a one-shot task and read the result back via `useCollection`.** This is NOT a chat — it's a single `query()` whose side effect is a written document. Use `capabilities: ['data_access']` to grant the data tools, and a `sessionKey` so this task's conversation stays isolated. With `refreshOnAgentComplete: true`, the result collection re-fetches automatically when the agent's write tool completes — no manual `refresh()` needed.
**Why write-to-collection instead of reading `toolCalls[].result`?** As of SDK 0.2.1, `toolCalls[].result` does carry the tool's structured return (and `toolCalls[].status` is `'error'` when a call fails), so it's fine for ephemeral/inline results. But the **durable** pattern is to have the agent persist a document and read it via `useCollection`: the result survives refreshes, is reactive across components, and is queryable later.
### Examples
**1. Read context in the app, PII-safe projection**
```tsx
// Read the shared pool the agent will reason over.
const pool = useCollection('records', { layer: 'shared', limit: 200 });
// Read the current user's OWN row with a dedicated 1-doc query — do NOT find() it
// inside the pool. The pool can settle a beat later, so `pool.documents.find(...)`
// reads as "missing" on first paint even when the row exists (a real, hard-won bug).
// A filtered limit:1 query resolves it independently of the pool load.
const mine = useCollection('records', {
layer: 'shared', filter: { owner_key: myKey }, limit: 1, refreshOnAgentComplete: true,
});
const self = mine.documents[0] ?? null;
const others = pool.documents.filter(r => r.owner_key !== myKey);
// PII-safe projection before the agent ever sees a row.
const toSafe = (r: Record) => ({
owner_key: r.owner_key, title: r.title, tags: r.tags,
// NO email, NO phone
});
```
**2. Deterministic prompt naming the exact document shape**
```tsx
function buildPrompt(self, others, nowIso: string) {
return [
`TASK — produce a result for owner_key "${self.owner_key}".`,
'Use the data provided below; do NOT query the database — it is all here.',
'',
'SUBJECT:', JSON.stringify(self, null, 2),
`CONTEXT (${others.length}):`, JSON.stringify(others, null, 2),
'',
'Steps:',
'1. Reason over the inputs.',
'2. WRITE exactly one document to the "results" collection with insert_documents.',
` Use this exact run_at (do NOT call a time tool): "${nowIso}". The document MUST be:`,
' { "owner_key": "...", "run_at": "...", "status": "fresh",',
' "items": [{ "ref", "score", "reasons": [] }] }',
'3. ALWAYS write the doc (even if items is empty). Never include email/phone.',
'Then reply with one short sentence summarizing the result.',
].join('\n');
}
```
**3. One-shot query, read result back reactively**
```tsx
const agent = useAgent('custom', { capabilities: ['data_access'], sessionKey: 'worker' });
// The result collection — reads back what the agent writes.
const results = useCollection('results', {
layer: 'user', // per-user results; no owner_key filter needed (partition is scoped)
sort: { run_at: -1 }, limit: 5,
refreshOnAgentComplete: true, // auto-refresh when the agent's insert_documents completes
});
async function run() {
await Promise.all([pool.refresh(), mine.refresh()]);
if (!self) return; // the 1-doc query may settle after first paint — gate on it
await agent.query(buildPrompt(toSafe(self), others.map(toSafe), new Date().toISOString()));
// results.documents[0] now holds the agent-written result (auto-refreshed). Render it directly.
}
const latest = results.documents[0];
const items = latest?.items ?? [];
```
**4. Upsert from the agent — ensure the row exists before you update it**
```tsx
// When an agent ENRICHES a record over a conversation, update_documents alone
// silently no-ops if the row doesn't exist yet — the user 'finishes' the chat but
// has no saved record (a real, hard-won bug). Tell the agent to UPSERT: insert a
// starter row first, then update it as it learns.
function enrichPrompt(ownerKey: string) {
return [
`ENRICH MODE. owner_key: "${ownerKey}".`,
'FIRST, query the "profiles" collection for this owner_key.',
'If there is NO doc, INSERT one now (insert_documents) as a starter row:',
` { "owner_key": "${ownerKey}", "fields": {}, "created_at": }`,
'THEN, as you learn facts, UPDATE that doc (update_documents, filter by',
'owner_key) and set updated_at. Keep nuanced observations as append-only notes.',
].join('\n');
}
// The app reflects the row passively — it never 'completes' on a chat marker.
// With refreshOnAgentComplete, the indicator flips once the row lands.
const mineP = useCollection('profiles', {
layer: 'shared', filter: { owner_key: myKey }, limit: 1, refreshOnAgentComplete: true,
});
const ready = !!mineP.documents[0];
```
> 🚫 **Critical:** **Never** parse the agent's chat text or `messages[]` for data — read the persisted document via `useCollection`.
> ℹ️ **Note:** MongoDB is schemaless: the agent can write any document shape (nested objects, arrays, optional/mixed fields) with no migration. The prompt defines the shape.
> ℹ️ **Note:** Use `capabilities: ['data_access']` to grant the agent its data tools, a `sessionKey` to isolate the task's conversation, and `refreshOnAgentComplete: true` on the result collection for automatic reactive read-back.
> 🚫 **Critical:** Read the **current user's own row** with a dedicated `useCollection({ filter: { owner_key }, limit: 1 })` query — not `pool.documents.find(...)`. The shared pool can settle after first paint, so finding 'self' inside it reads as absent on the first render even when the row exists.
> 🚫 **Critical:** When an agent **enriches** a record over a conversation, instruct it to **upsert**: query for the row, `insert_documents` a starter row if missing, then `update_documents` as it learns. A bare `update_documents` no-ops on a missing row — the user finishes the chat with no saved record.
**See also:** [useAgent](https://flowstack.fun/docs/useAgent) · [useCollection](https://flowstack.fun/docs/useCollection) · [two-layer-data-model](https://flowstack.fun/docs/two-layer-data-model)
*Source: README.md#agent-driven-data-models--reason--write--read*
## Two-layer data model — shared vs per-user
*guide* — How useCollection and the agent's data tools resolve a collection to one of two namespaces — a physically isolated per-user database or a single world-readable shared pool — via the layer option.
`useCollection` (and the agent's data tools) resolve a collection to one of two namespaces via the `layer` option:
- **`layer: 'user'`** (per-user) — each end-user gets their own **physically isolated** MongoDB database (`u_{sha256(user_id)[:16]}`), not just a filtered view. The backend always keys this DB off the *requesting* user, so one user literally cannot read or write another user's partition through any path. Reads are already scoped — do **not** add a `user_id`/key filter. Use for anything truly private: results, drafts, private history (e.g. `matches`).
- **`layer: 'shared'`** — one builder database (`b_{...}`, one per app) that **every signed-in user reads** (e.g. a directory, catalog, leaderboard). Writes are restricted: the app cannot `insert()` directly; a collection marked **`agent_writable`** in the data plan lets the app's own agent append (e.g. a registrar writing member profiles), while blocking arbitrary end-user writes.
- **`layer: 'auto'`** (default) — resolves from the collection's configured data model.
**Security — `shared` is not access-controlled per user.** Rows in a shared collection carry a `_flowstack.user_id` field, but that is a *filterable field, not an enforced read ACL* — any signed-in user can read every row. Treat a shared collection as a public bulletin board at the storage layer; never store data one user must not see from another there. Only the `'user'` layer is private.
**Platform gap — no cross-user delivery (today).** Because the per-user DB is always keyed to the requester, you cannot write into *another* user's private partition. Patterns that deliver data privately *to* a specific recipient — direct messages, per-user notifications — are not achievable: the only private store (`'user'`) is unreachable cross-user, and the only cross-user store (`'shared'`) is world-readable. The correct design (deliver into the recipient's per-user partition) needs a cross-user write primitive the platform does not yet expose.
Reads of a shared collection via `useCollection({ layer: 'shared' })` always hit the shared pool. Agent-side reads of shared collections are also routed correctly as of P0-132, but the **recommended pattern remains "read in the app, pass into the prompt"** — it's the most reliable and keeps PII out of the agent.
**File Upload → MongoDB:** parse a file in the app and `insert()` the rows directly via `useCollection`; all `useCollection` instances for that collection auto-refresh.
### Examples
**File upload to MongoDB via useCollection**
```tsx
const { insert } = useCollection('transactions');
const handleCSVUpload = async (file: File) => {
const text = await file.text();
const rows = Papa.parse(text, { header: true }).data;
await insert(rows.map(row => ({
...row,
amount: Number(row.amount),
imported_at: new Date().toISOString(),
})));
// All useCollection('transactions') instances auto-refresh
};
```
> ℹ️ **Note:** `layer: 'user'` reads are already scoped to the current user — do **not** add a `user_id`/key filter. The partition is a separate physical MongoDB database (`u_{hash}`), always keyed to the requesting user.
> 🚫 **Critical:** **`shared` is world-readable.** The `_flowstack.user_id` field on shared rows is a filter, not an access-control boundary — any signed-in user can read every row. Never store private data (messages, PII, secrets) in a `shared` collection, even filtered by user. Only `layer: 'user'` is private.
> ⚠️ **Warning:** **No cross-user delivery primitive yet.** You cannot write into another user's per-user partition, so private per-recipient delivery (DMs, targeted notifications) isn't achievable with current layers — the private store is unreachable cross-user and the shared store is world-readable.
> ⚠️ **Warning:** The app cannot `insert()` directly into a `shared` collection. Only an agent acting on a collection marked `agent_writable` in the data plan may append to the shared pool; arbitrary end-user writes stay blocked.
> ℹ️ **Note:** Recommended pattern remains "read in the app, pass into the prompt" — most reliable and keeps PII out of the agent.
**See also:** [useCollection](https://flowstack.fun/docs/useCollection) · [reason-write-read](https://flowstack.fun/docs/reason-write-read) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#two-layer-data-model--shared-vs-per-user*
## form-write-direct
*recipe · since 0.2.8* — Save form submissions and simple CRUD deterministically — no LLM, no tokens, no streaming
**Plane:** data_pattern
**Steps:**
1. `useCollection` — For plain saves: call insert/update/remove directly — deterministic, instant, free.
2. `useToolInvocation` — Only when you need a SPECIFIC agent tool deterministically (e.g. a registered agent's mongodb_query) — direct tool call, still no LLM.
### Examples
**End-to-end example**
```tsx
import { useState } from 'react';
import { useCollection } from '@flowstack/sdk';
export function ContactForm() {
const [email, setEmail] = useState('');
const [msg, setMsg] = useState('');
const { insert } = useCollection('submissions', { layer: 'shared' });
return (
);
}
```
> ⚠️ **Warning:** Never route form saves through useAgent — an LLM in the write path adds latency, cost, and nondeterminism for zero value.
> ⚠️ **Warning:** useToolInvocation targets one agent tool by name (agentName + toolName) and blocks concurrent calls to the same tool.
> ⚠️ **Warning:** Validate in the component before insert; the collection is schemaless and will accept whatever you send.
**See also:** [per-user-store](https://flowstack.fun/docs/per-user-store) · [shared-pool](https://flowstack.fun/docs/shared-pool) · [reason-write-read](https://flowstack.fun/docs/reason-write-read) · [useToolInvocation](https://flowstack.fun/docs/useToolInvocation) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## library-import-analyze
*recipe · since 0.2.8* — Import a workspace library item (dataset, report, document, visualization) into the app's agent and analyze it
**Plane:** artifact · **Requires:** capability `data_access`
**Steps:**
1. `casino_list_library` — Discover importable items (report | visualization | document | dataset) in the owner's workspace library.
2. `casino_import_from_library` — Copy the item into the site agent's workspace so builds and agent runs can reference it.
3. `casino_talk_to_agent` — Pass the imported file in file_refs and instruct the agent to persist its analysis into a collection.
4. `useCollection` — The app reads the analysis collection reactively — the imported file itself never ships to the browser.
### Examples
**End-to-end example**
```ts
// Build-time (MCP path — Claude driving the casino tools):
// 1. casino_list_library({ item_type: "dataset" })
// 2. casino_import_from_library({ site_id, item_id })
// 3. casino_talk_to_agent({
// site_id,
// agent_name: "analyst",
// message: "Analyze the imported sales dataset; insert one doc per finding into " +
// "findings: {title, detail, metric, created_at (ISO)}.",
// file_refs: ["sales_2026.csv"],
// })
//
// App-side read (tsx):
// const { documents } = useCollection('findings', {
// sort: { created_at: -1 },
// refreshOnAgentComplete: true,
// });
```
> ⚠️ **Warning:** Import copies the item into the SITE's agent workspace — the app cannot read library items directly.
> ⚠️ **Warning:** Always persist analysis into a collection; file_refs content is visible to the agent run only.
> ⚠️ **Warning:** Library items are owner-scoped: end users see derived collection docs, never the raw file.
**See also:** [reason-write-read](https://flowstack.fun/docs/reason-write-read) · [casino_list_library](https://flowstack.fun/docs/casino_list_library) · [casino_import_from_library](https://flowstack.fun/docs/casino_import_from_library) · [casino_talk_to_agent](https://flowstack.fun/docs/casino_talk_to_agent) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## per-user-store
*recipe · since 0.2.8* — Give each signed-in user their own private data (notes, entries, history) isolated from every other user
**Plane:** data_pattern
**Steps:**
1. `useCollection` — Pass { layer: 'user' } — each user's documents live in a physically isolated per-user DB; no user_id filtering needed.
2. `useCollection` — Use insert/update/remove from the same hook for direct CRUD; the agent is not involved.
### Examples
**End-to-end example**
```tsx
import { useState } from 'react';
import { useCollection } from '@flowstack/sdk';
export function Notes() {
const [draft, setDraft] = useState('');
const { documents: notes, insert, remove } = useCollection('notes', {
layer: 'user',
sort: { created_at: -1 },
});
return (
<>
setDraft(e.target.value)} />
{notes.map(n => (
{n.text}
))}
>
);
}
```
> ⚠️ **Warning:** layer 'user' is PHYSICAL isolation — do not also filter by user_id; there is no cross-user leakage to defend against.
> ⚠️ **Warning:** Per-user data is invisible to other users AND to the shared pool; use layer 'shared' for anything communal.
> ⚠️ **Warning:** Direct CRUD via useCollection needs no agent and no capabilities — don't route simple writes through useAgent.
**See also:** [shared-pool](https://flowstack.fun/docs/shared-pool) · [form-write-direct](https://flowstack.fun/docs/form-write-direct) · [reason-write-read](https://flowstack.fun/docs/reason-write-read) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## scheduled-agent-report
*recipe · since 0.2.8* — Run an agent prompt on a cron schedule and surface its output in the app (daily digests, weekly reports)
**Plane:** data_pattern · **Requires:** capability `data_access`
**Steps:**
1. `useAutomations` — create({ name, prompt, schedule: '0 8 * * 1-5', target_agents, output_config }) — 5-field Unix cron via EventBridge; output types: silent | email | webhook | file.
2. `useAgent` — Write the automation prompt to end with a structured collection write (same discipline as reason-write-read) so the app can render results.
3. `useCollection` — Read the report collection reactively; each run appends a new doc.
### Examples
**End-to-end example**
```tsx
import { useAutomations, useCollection } from '@flowstack/sdk';
export function DailyDigest() {
const { automations, create, pause, resume } = useAutomations();
const digest = automations.find(a => a.name === 'daily-digest');
const { documents: reports } = useCollection('daily_reports', {
sort: { run_at: -1 },
limit: 7,
});
return (
<>
{!digest && (
)}
{digest && (
)}
{reports.map(r => {r.summary})}
>
);
}
```
> ⚠️ **Warning:** Schedules are 5-field Unix cron on EventBridge — validate the expression; a bad field silently never fires.
> ⚠️ **Warning:** End the automation prompt with an explicit collection write; 'silent' output is otherwise invisible to the app.
> ⚠️ **Warning:** Pause/resume instead of delete — runs history stays attached to the automation.
**See also:** [reason-write-read](https://flowstack.fun/docs/reason-write-read) · [useAutomations](https://flowstack.fun/docs/useAutomations) · [useCollection](https://flowstack.fun/docs/useCollection) · [casino_create_automation](https://flowstack.fun/docs/casino_create_automation)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## shared-pool
*recipe · since 0.2.8* — Share one pool of data across all users of the app (leaderboards, feeds, catalogs), readable even before sign-in
**Plane:** data_pattern
**Steps:**
1. `useCollection` — Pass { layer: 'shared' } for signed-in reads/writes into the app-wide pool.
2. `usePublicCollection` — Use for read-only access WITHOUT sign-in (landing pages, public leaderboards).
### Examples
**End-to-end example**
```tsx
import { useCollection, usePublicCollection } from '@flowstack/sdk';
export function Leaderboard({ signedIn }: { signedIn: boolean }) {
// Public, pre-auth read:
const { documents: publicRows } = usePublicCollection('scores', {
sort: { points: -1 },
limit: 10,
});
// Signed-in write path:
const { insert } = useCollection('scores', { layer: 'shared' });
return (
<>
{publicRows.map(r =>
{r.player}: {r.points}
)}
{signedIn && (
)}
>
);
}
```
> ⚠️ **Warning:** layer 'shared' is world-readable within the app — never put private or per-user data here.
> ⚠️ **Warning:** usePublicCollection is read-only and needs no auth; writes always go through useCollection with sign-in.
> ⚠️ **Warning:** Seed shared catalogs via the agent or an import, not by hardcoding rows in the app source.
**See also:** [per-user-store](https://flowstack.fun/docs/per-user-store) · [form-write-direct](https://flowstack.fun/docs/form-write-direct) · [usePublicCollection](https://flowstack.fun/docs/usePublicCollection) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## useCollection
*hook · since 0.1.0* — Direct MongoDB CRUD for built-app data — the primary, reactive data hook for tables, forms, cards, and persistence.
```ts
import { useCollection } from 'flowstack-sdk'
```
The primary data hook — use it for all tables, forms, cards, and data persistence. The agent is NOT involved in data operations. Collection names are auto-prefixed with the site_id by the backend, so pass SHORT names only (e.g. `'transactions'`, not `'site_abc__transactions'`). Reads are reactive: they auto-fetch on mount and re-fetch after mutations; every `useCollection` instance for the same collection auto-refreshes after a successful write.
```ts
const { documents, count, total, isLoading, error, refresh, insert, update, remove } = useCollection(name, options?)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `name` | `string` | required | Short collection name (auto-prefixed with site_id by the backend). |
| `options.filter` | `Filter` | — | MongoDB query filter. |
| `options.sort` | `Record` | — | Sort spec, e.g. `{ date: -1 }`. |
| `options.limit` | `number` | default `50` | Max documents to return (max 500). |
| `options.skip` | `number` | default `0` | Pagination offset. |
| `options.layer` | `'user' | 'shared' | 'auto'` | default `'auto'` | Namespace: `'user'` is a **physically isolated** per-user database (already scoped — don't filter by a user key; no other user can read it); `'shared'` is one pool **every signed-in user can read** (app can't insert directly unless the collection is `agent_writable`) — it is NOT access-controlled per user; `'auto'` resolves from the collection's data model. See [two-layer-data-model](two-layer-data-model). |
| `options.refreshOnAgentComplete` | `boolean` | since 0.2.1 | Auto-refresh after an agent write tool (insert_documents/update_documents/insert_app_data/etc.) completes — the reactive bridge for the Reason→Write→Read pattern. |
### Returns
| Field | Type | Description |
|---|---|---|
| `documents` | `T[]` | The fetched documents. |
| `count` | `number` | Documents returned (≤ limit). |
| `total` | `number` | Total matching documents (for pagination). |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `refresh` | `() => Promise` | Manual re-fetch. |
| `insert` | `(doc: T | T[]) => Promise<{ inserted_ids: string[] }>` | Insert one or many documents. |
| `update` | `(filter, update, opts?) => Promise<{ modified_count: number }>` | Update matching documents. |
| `remove` | `(filter) => Promise<{ deleted_count: number }>` | Delete matching documents. |
### Examples
**Reactive query**
```tsx
function TransactionTable() {
const { documents, isLoading } = useCollection('transactions', {
sort: { date: -1 }, limit: 50, refreshOnAgentComplete: true,
});
if (isLoading) return
Loading...
;
return (
{documents.map(row => (
{row.date}
${row.amount}
))}
);
}
```
**Insert on form submit**
```tsx
const { insert } = useCollection('transactions');
await insert({ ...form, amount: Number(form.amount), created_at: new Date().toISOString() });
// All useCollection('transactions') instances auto-refresh
```
**Update and delete**
```tsx
const { update, remove } = useCollection('transactions');
await update({ _id: docId }, { $set: { category: 'Food' } });
await remove({ _id: docId });
```
> 🚫 **Critical:** The app **reads** all data with `useCollection` (tables, forms, cards, CRUD) — never parse the agent's chat text or `messages[]` for data, and never use `useToolInvocation`. **Writes** happen either via deterministic user actions (`insert`/`update`/`remove`) or via the agent's `data_access` tools followed by a `useCollection` read (Reason → Write → Read).
> 🚫 **Critical:** **`layer: 'shared'` is world-readable.** Every signed-in user can read every row; the `_flowstack.user_id` field is a filter, not an ACL. Put anything one user must not see from another in `layer: 'user'` (a physically isolated DB). And note you **cannot** write into another user's per-user partition — there's no cross-user delivery primitive (no private DMs/notifications today). See [two-layer-data-model](two-layer-data-model).
**See also:** [usePublicCollection](https://flowstack.fun/docs/usePublicCollection) · [useAgent](https://flowstack.fun/docs/useAgent) · [useDatasets](https://flowstack.fun/docs/useDatasets) · [useQuery](https://flowstack.fun/docs/useQuery) · [two-layer-data-model](https://flowstack.fun/docs/two-layer-data-model)
*Source: README.md#usecollection*
## useCollectionExplorer
*hook* — Browse, query, export, and delete a specific MongoDB collection.
```ts
import { useCollectionExplorer } from 'flowstack-sdk'
```
Browse, query, export, and delete a specific MongoDB collection. Exported from `flowstack-sdk` and sharing the same `FlowstackProvider` context.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useCollectionExplorer.ts` as the source of truth.
**See also:** [useCollection](https://flowstack.fun/docs/useCollection) · [useUserCollections](https://flowstack.fun/docs/useUserCollections) · [useDataOverview](https://flowstack.fun/docs/useDataOverview)
*Source: README.md#additional-hooks-05*
## useDataOverview
*hook* — Get a unified summary of all user-owned data.
```ts
import { useDataOverview } from 'flowstack-sdk'
```
Unified summary of all user-owned data. Exported from `flowstack-sdk` and sharing the same `FlowstackProvider` context.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useDataOverview.ts` as the source of truth.
**See also:** [useUserCollections](https://flowstack.fun/docs/useUserCollections) · [useCollectionExplorer](https://flowstack.fun/docs/useCollectionExplorer) · [useDatasets](https://flowstack.fun/docs/useDatasets)
*Source: README.md#additional-hooks-05*
## useDatasets
*hook · ⚠ not for built apps* — List, upload, download, and delete datasets, with reactive loading and error state.
```ts
import { useDatasets } from 'flowstack-sdk'
```
Dataset operations.
```ts
const { datasets, isLoading, error, uploadDataset, downloadDataset, deleteDataset, refreshDatasets } = useDatasets()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `datasets` | `DatasetInfo[]` | Datasets, each `{ id, name, rows, columns, schema?, columnNames?, createdAt?, updatedAt? }`. |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `uploadDataset` | `(file, name?) => Promise` | Upload a dataset file, optionally naming it. |
| `downloadDataset` | `(name) => Promise` | Download a dataset by name. |
| `deleteDataset` | `(name) => Promise` | Delete a dataset by name. |
| `refreshDatasets` | `() => Promise` | Manual re-fetch. |
### Examples
```tsx
const {
datasets, // DatasetInfo[] — { id, name, rows, columns, schema?, columnNames?, createdAt?, updatedAt? }
isLoading, // boolean
error, // string | null
uploadDataset, // (file, name?) => Promise
downloadDataset, // (name) => Promise
deleteDataset, // (name) => Promise
refreshDatasets, // () => Promise
} = useDatasets();
```
> ℹ️ **Note:** DatasetInfo fields (source of truth: `src/types/index.ts`): `id: string` — stable identifier; `name: string` — user-visible name; `rows: number` — row count (NOT `rowCount`); `columns: number` — column count (NOT `columnCount`); `schema?: Record` — optional per-column schema; `columnNames?: string[]` — optional ordered column names; `createdAt?: string`, `updatedAt?: string` — ISO8601 timestamps.
**See also:** [useCollection](https://flowstack.fun/docs/useCollection) · [useVisualizations](https://flowstack.fun/docs/useVisualizations) · [useReports](https://flowstack.fun/docs/useReports) · [useDataSources](https://flowstack.fun/docs/useDataSources) · [useQuery](https://flowstack.fun/docs/useQuery)
*Source: README.md#usedatasets*
## useDataSources
*hook · ⚠ not for built apps* — Manage external data source connections — create, test, delete, and list.
```ts
import { useDataSources } from 'flowstack-sdk'
```
External data source connections.
Supports: PostgreSQL, MongoDB, MySQL, Snowflake, BigQuery, S3.
```ts
const { dataSources, isLoading, error, createDataSource, testConnection, deleteDataSource, refreshDataSources } = useDataSources()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `dataSources` | `DataSource[]` | Configured external data source connections. |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `createDataSource` | `(config) => Promise` | Create a new external data source connection. |
| `testConnection` | `(sourceId) => Promise` | Test connectivity to a data source. |
| `deleteDataSource` | `(sourceId) => Promise` | Delete a data source connection. |
| `refreshDataSources` | `() => Promise` | Manual re-fetch. |
### Examples
```tsx
const {
dataSources, // DataSource[]
isLoading, // boolean
error, // string | null
createDataSource, // (config) => Promise
testConnection, // (sourceId) => Promise
deleteDataSource, // (sourceId) => Promise
refreshDataSources,// () => Promise
} = useDataSources();
```
> ℹ️ **Note:** Supports: PostgreSQL, MongoDB, MySQL, Snowflake, BigQuery, S3.
**See also:** [useDatasets](https://flowstack.fun/docs/useDatasets) · [useQuery](https://flowstack.fun/docs/useQuery)
*Source: README.md#usedatasources*
## usePublicCollection
*hook* — Anonymous, no-auth public collection reads and inserts for leaderboards, guestbooks, comment threads, and voting.
```ts
import { usePublicCollection } from 'flowstack-sdk'
```
Anonymous public submissions — leaderboards, guestbooks, comment threads, voting. **No auth required.** Any visitor can read and insert. The collection must be declared in `app_config.publicCollections`.
**Key differences from `useCollection`:**
- No credentials needed — works for anonymous visitors
- Insert only (no update/remove — data is owned by the app, not the user)
- Rate-limited server-side (default 10 writes/min, 100/day per IP)
- The collection must be in `app_config.publicCollections` (ask the builder to configure it; the build pipeline sets this up based on `usePublicCollection` usage in the source)
```ts
const { documents, count, total, isLoading, error, insert, refresh } = usePublicCollection(name, options?)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `name` | `string` | required | Public collection name (must be declared in `app_config.publicCollections`). |
| `options.filter` | `Filter` | — | MongoDB query filter, e.g. `{ album: 'yeezus' }`. |
| `options.sort` | `Record` | — | Sort spec, e.g. `{ score: -1 }`. |
| `options.limit` | `number` | — | Max documents to return. |
### Returns
| Field | Type | Description |
|---|---|---|
| `documents` | `T[]` | The fetched documents. |
| `count` | `number` | Documents returned. |
| `total` | `number` | Total matching documents. |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `insert` | `(doc: Partial) => Promise<{ inserted_id: string }>` | Insert a public document (rate-limited per IP). |
| `refresh` | `() => Promise` | Manual re-fetch. |
### Examples
**Anonymous leaderboard**
```tsx
import { usePublicCollection } from 'flowstack-sdk';
const {
documents, // T[]
count, // number
total, // number
isLoading, // boolean
error, // string | null
insert, // (doc: Partial) => Promise<{ inserted_id: string }>
refresh, // () => Promise
} = usePublicCollection('high_scores', {
filter: { album: 'yeezus' },
sort: { score: -1 },
limit: 25,
});
await insert({ album: 'yeezus', name: 'KEON', score: 12500 });
```
**Builder config required in app_config.json**
```json
{
"publicCollections": {
"high_scores": {
"schema": {
"album": { "type": "string", "required": true, "maxLength": 32 },
"name": { "type": "string", "required": true, "maxLength": 24 },
"score": { "type": "number", "required": true, "min": 0 }
},
"rateLimit": { "writesPerMinute": 5, "writesPerDay": 50 }
}
}
}
```
> ℹ️ **Note:** No auth required — any visitor can read and insert. Insert only: there is no update or remove because data is owned by the app, not the user.
> ⚠️ **Warning:** Writes are rate-limited server-side (default 10 writes/min, 100/day per IP). The collection must be declared in `app_config.publicCollections`.
> 🚫 **Critical:** **Requires `tenantId` on `FlowstackProvider`.** Anonymous visitors have no token, so the backend can't derive the tenant — this is the one hook that needs `tenantId` set explicitly. If it's missing, the hook raises a clear error instead of silently using a default. Authenticated hooks don't need it (tenant comes from the JWT).
**See also:** [useCollection](https://flowstack.fun/docs/useCollection) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#usepubliccollection*
## useQuery
*hook* — Run a single one-shot query from a natural-language prompt without streaming.
```ts
import { useQuery } from 'flowstack-sdk'
```
Simple one-shot query execution (no streaming).
```ts
const { executeQuery, result, isLoading, error } = useQuery()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `executeQuery` | `(prompt: string) => Promise` | Execute a one-shot query from a natural-language prompt. |
| `result` | `QueryResult | null` | The latest query result. |
| `isLoading` | `boolean` | Query in flight. |
| `error` | `string | null` | Last error message. |
### Examples
```tsx
const {
executeQuery, // (prompt: string) => Promise
result, // QueryResult | null
isLoading, // boolean
error, // string | null
} = useQuery();
```
**See also:** [useCollection](https://flowstack.fun/docs/useCollection) · [useDatasets](https://flowstack.fun/docs/useDatasets) · [useDataSources](https://flowstack.fun/docs/useDataSources)
*Source: README.md#usequery*
## useReports
*hook · ⚠ not for built apps* — List generated reports with reactive loading and error state.
```ts
import { useReports } from 'flowstack-sdk'
```
Report management.
```ts
const { reports, isLoading, error, refreshReports } = useReports()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `reports` | `ReportInfo[]` | Reports, each `{ name, content, format, createdAt }`. |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `refreshReports` | `() => Promise` | Manual re-fetch. |
### Examples
```tsx
const {
reports, // ReportInfo[] — { name, content, format, createdAt }
isLoading, // boolean
error, // string | null
refreshReports, // () => Promise
} = useReports();
```
**See also:** [useDatasets](https://flowstack.fun/docs/useDatasets) · [useVisualizations](https://flowstack.fun/docs/useVisualizations) · [useQuery](https://flowstack.fun/docs/useQuery)
*Source: README.md#usereports*
## useUserCollections
*hook* — List all MongoDB collections the user owns.
```ts
import { useUserCollections } from 'flowstack-sdk'
```
List all MongoDB collections the user owns. Exported from `flowstack-sdk` and sharing the same `FlowstackProvider` context.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useUserCollections.ts` as the source of truth.
**See also:** [useCollection](https://flowstack.fun/docs/useCollection) · [useCollectionExplorer](https://flowstack.fun/docs/useCollectionExplorer) · [useDataOverview](https://flowstack.fun/docs/useDataOverview)
*Source: README.md#additional-hooks-05*
## useVisualizations
*hook · ⚠ not for built apps* — List generated visualizations with reactive loading and error state.
```ts
import { useVisualizations } from 'flowstack-sdk'
```
Visualization management.
```ts
const { visualizations, isLoading, error, refreshVisualizations } = useVisualizations()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `visualizations` | `VisualizationData[]` | Visualizations, each `{ name, type, imageUrl, imageBase64, format, createdAt }`. |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `refreshVisualizations` | `() => Promise` | Manual re-fetch. |
### Examples
```tsx
const {
visualizations, // VisualizationData[] — { name, type, imageUrl, imageBase64, format, createdAt }
isLoading, // boolean
error, // string | null
refreshVisualizations,// () => Promise
} = useVisualizations();
```
**See also:** [useDatasets](https://flowstack.fun/docs/useDatasets) · [useReports](https://flowstack.fun/docs/useReports) · [useQuery](https://flowstack.fun/docs/useQuery)
*Source: README.md#usevisualizations*
---
# AI & Agents
Streaming agent queries, personas, tool access, and the agent catalog.
## Agent Catalog
*guide* — There are no pre-built ecosystem agents — agents are per-app; you create them with casino_create_agent via the Casino MCP and target them by name.
There are no pre-built ecosystem agents. Agents are per-app — you create them with `casino_create_agent` via the Casino MCP, then select them with the `persona` option on `useAgent`.
The backend reads the agent definition from your app's config, injects the system prompt, and enforces the declared capabilities — the client cannot escalate to broader tool access.
Note: the SDK's `dataScienceTemplate`/`marketingTemplate`/`supportTemplate` (see Agent Templates & Factory) are client-side config scaffolds for `ChatInterface`, **not** registered backend agents — "no pre-built agents" refers to the backend persona catalog.
### Examples
**Creating an agent (MCP) — category grant**
```ts
casino_create_agent(
site_id="",
name="support_bot",
system_prompt="You are a helpful support assistant...",
capabilities=["data_access"] // scopes what tools this agent can use
)
```
**Creating an agent (MCP) — per-tool grant**
```ts
casino_create_agent(
site_id="",
name="readonly_bot",
system_prompt="You are a read-only support assistant...",
tools=["query_mongodb", "count_documents"] // only these 2 tools, no writes
)
```
**Targeting it from your app**
```tsx
const { query, messages } = useAgent(undefined, {
persona: 'support_bot',
capabilities: ['data_access'],
});
```
**See also:** [targeting-custom-agents](https://flowstack.fun/docs/targeting-custom-agents) · [tool-categories](https://flowstack.fun/docs/tool-categories) · [tool-access-control](https://flowstack.fun/docs/tool-access-control) · [useAgent](https://flowstack.fun/docs/useAgent) · [useAgents](https://flowstack.fun/docs/useAgents)
*Source: README.md#agent-catalog*
## Agent Templates & Factory
*guide* — Pre-configured agent templates (data-science, marketing, support) plus a dynamic Agent Factory that routes user intent to the best registered agent.
**Pre-configured Templates** — import ready-made templates (`dataScienceTemplate`, `marketingTemplate`, `supportTemplate`) and use them directly with `ChatInterface`, or build your own with `createCustomTemplate`.
**Agent Factory (Dynamic Routing)** — register agents with descriptions, templates, and matching patterns in an `AgentRegistry`, then use `IntentAnalyzer` to classify a user message and route it to the best-fitting agent.
### Examples
**Pre-configured Templates**
```tsx
import { dataScienceTemplate, marketingTemplate, supportTemplate, createCustomTemplate } from 'flowstack-sdk';
// Use with ChatInterface
// Custom template
const custom = createCustomTemplate({
streaming: true,
networkMode: 'PUBLIC',
systemPrompt: 'You are a helpful assistant for ...',
});
```
**Agent Factory (Dynamic Routing)**
```tsx
import { AgentFactory, IntentAnalyzer, AgentRegistry, DEFAULT_PATTERNS } from 'flowstack-sdk';
// Register agents
const registry = new AgentRegistry();
registry.register({
name: 'data-analyst',
description: 'Analyzes datasets and creates visualizations',
template: dataScienceTemplate,
patterns: [/analyz/i, /chart/i, /data/i],
});
// Analyze user intent and route to best agent
const analyzer = new IntentAnalyzer({ registry });
const intent = analyzer.analyze('Show me a chart of Q4 revenue');
// → { category: 'data-analysis', confidence: 0.92, entities: ['Q4 revenue'] }
```
> ℹ️ **Note:** **Two agent layers — which to use.** This Factory/Templates layer is **client-side**: templates are `ChatInterface` presets and `AgentFactory`/`IntentAnalyzer`/`AgentRegistry` route a user message to a template in the browser. The **backend persona** system (`casino_create_agent` + the `persona` option on `useAgent`) is what actually scopes tools/capabilities and is enforced server-side. For built apps, prefer backend personas (`persona`) for anything touching data or tools; use client-side Factory routing only to pick a chat *style/template* among personas you've already registered. They compose: route with the Factory, then pass the chosen persona name to `useAgent({ persona })`.
**See also:** [useAgent](https://flowstack.fun/docs/useAgent) · [ChatInterface](https://flowstack.fun/docs/ChatInterface) · [agent-catalog](https://flowstack.fun/docs/agent-catalog)
*Source: README.md#agent-templates--factory*
## Targeting Custom Agents (Built Apps)
*guide* — Built apps target an agent they create via `casino_create_agent` by passing its registered name as `persona` — a target is required for app-scoped users or they get a 403.
For built apps, the agent you target is one **you create** via `casino_create_agent`. There are no pre-built ecosystem agents — you define the persona and capabilities, and the platform enforces them. (The SDK's `dataScienceTemplate`/`marketingTemplate`/`supportTemplate` are client-side config scaffolds, not registered agents.)
Create the agent once via MCP at setup time, then select it from your built app with the `persona` option. The registered persona's server-side definition is authoritative — `capabilities`/`tools` passed to `useAgent` are advisory and can only narrow within it, never widen.
### Examples
**Create then target a custom agent**
```tsx
// 1. Create your agent via MCP first (once, at setup time):
// casino_create_agent(site_id, "support_bot", "You are a helpful support assistant...", capabilities=["data_access"])
// OR for per-tool control: casino_create_agent(site_id, "support_bot", "...", tools=["query_mongodb", "count_documents"])
// 2. Target it from your built app:
const { query, messages, isStreaming } = useAgent(undefined, {
persona: 'support_bot', // must match the name you registered
capabilities: ['data_access'], // advisory — the registered persona's grant is authoritative
});
```
> 🚫 **Critical:** A target persona is required for app-scoped users (`appScope` set) — pass `persona: ''`. Without a target, app-scoped users get a 403. The name must exactly match a `casino_create_agent` registration for your site. (`targetAgents` is a deprecated alias — see useAgent.)
**See also:** [useAgent](https://flowstack.fun/docs/useAgent) · [useAgents](https://flowstack.fun/docs/useAgents) · [agent-catalog](https://flowstack.fun/docs/agent-catalog) · [tool-categories](https://flowstack.fun/docs/tool-categories)
*Source: README.md#targeting-custom-agents-built-apps*
## Tool-level Access Control (P0-117)
*guide* — Pass an explicit tools list to casino_create_agent for per-tool access control, which overrides category-level capabilities grants.
In addition to `capabilities` (category grants), you can pass an explicit `tools` list to `casino_create_agent` for per-tool access control:
- `capabilities` — category granularity; all tools in that group (e.g. `["data_access"]` → all 14 data tools).
- `tools` — individual tool names (e.g. `["query_mongodb", "count_documents"]` → only those 2).
**Priority:** `tools` (specific) overrides `capabilities` (category). If only `capabilities` is set, P0-116 behavior is unchanged.
Use `casino_list_tools()` first to see all valid tool names and categories, then pass exact names.
### Examples
**Discover tools, then create a read-only bot**
```ts
// Step 1 — discover available tool names:
casino_list_tools()
// → returns { "data": ["query_mongodb", "insert_documents", ...], "capabilities": [...], ... }
// Step 2 — create a read-only support bot (no write access):
casino_create_agent(
site_id="",
name="support_bot",
system_prompt="You are a helpful support assistant...",
tools=["query_mongodb", "count_documents", "describe_collection"]
)
// → capabilities auto-derived as ["data_access"], tools enforced at runtime
```
> ℹ️ **Note:** **Always-on tool exemption:** `web_search` (basic), `create_diagram`, `list_sites`, `get_site_files`, and the category meta-tools are never filtered — only category-loaded tools are subject to the allowlist. (The gated, heavier counterpart is `web_search_deep`, which requires `external_integration`.)
**See also:** [tool-categories](https://flowstack.fun/docs/tool-categories) · [targeting-custom-agents](https://flowstack.fun/docs/targeting-custom-agents) · [agent-catalog](https://flowstack.fun/docs/agent-catalog)
*Source: README.md#tool-level-access-control-p0-117*
## Available Tool Categories
*guide* — The nine capability categories you declare on a registered agent (and, advisorily, on `useAgent`), each granting a group of related tools.
Declare these in `capabilities` when registering an agent via `casino_create_agent`. (For persona-backed apps the registered definition is authoritative — categories passed to `useAgent` are advisory and cannot widen the persona's grant.)
- `data_access` — query_mongodb, insert_documents, update_documents, aggregate_mongodb, count_documents, list_collections, query_data_source, and more
- `external_integration` — web_search_deep, google_ads, google_analytics (GA4), google_drive, twitter, reddit, youtube, strava, kaggle, call_integration, send_app_sms, register_app_phone
- `site_operations` — build_project, write_project_file, read_project_file, get_site_files, publish_version, edit_site, import_github_repo
- `code_execution` — python_exec, daytona_run_code, materialize_in_sandbox, claude_code_exec
- `domain_task` — finance/health/legal/tax domain tools, fetch_daily_brief, text_extract_pattern
- `workspace_management` — save_document, list_documents, search_documents, save_report, save_visualization, remember, search_memory
- `agent_management` — spawn_subagent, create_subagent, list_subagents, list_subagent_runs
- `specialist_agents` — data_science, legal_review, app_building, tax_analysis, accounting, route_to_agent
- `automation_management` — create_automation, list_automations, pause/resume/delete/run automations
Most built-app agents only need `data_access`. Note `web_search` (basic) is always available and is **not** gated — only `web_search_deep` requires `external_integration`. Run `casino_list_tools` (MCP) for the live, authoritative tool list per category.
**See also:** [targeting-custom-agents](https://flowstack.fun/docs/targeting-custom-agents) · [tool-access-control](https://flowstack.fun/docs/tool-access-control) · [agent-catalog](https://flowstack.fun/docs/agent-catalog)
*Source: README.md#available-tool-categories*
## useAgent
*hook · since 0.1.0* — Run AI agent queries with streaming, tool calls, interrupts, status updates, and automatic conversation persistence.
```ts
import { useAgent } from 'flowstack-sdk'
```
`useAgent` is the primary AI hook: it runs agent queries with token streaming, surfaces active/completed tool calls and connected data sources, and supports mid-execution interrupts.
**Template (first arg):** `'data-science'` | `'marketing'` | `'support'` | `'custom'`. For a **persona-backed app** (your app's brain is an agent registered via `casino_create_agent`), the template is just a fallback label — the registered persona overrides it. Use `'custom'` (or omit) and select the brain with the `persona` option. The template does **not** pick the agent.
**Conversation memory (automatic):** `useAgent` persists a stable **session ID** for each component mount — generated on first use via `crypto.randomUUID()` (with a timestamp fallback for legacy browsers), stable across turns (every `query()` passes the same session ID), and reused by Strands' `S3SessionManager` so the agent remembers prior messages, tool calls, and context across turns. `clearMessages()` resets the session, generating a fresh session ID on the next `query()`. The session ID is tied to the component mount: unmounting/remounting (e.g. closing and reopening a modal) starts a new conversation. The simplest path (0.2.1+) for cross-reload persistence is to pass a stable `sessionKey` per surface; for cross-tab or multi-conversation persistence, manage the session ID at the application level (e.g. in `localStorage`). Wallet-authenticated users have conversations scoped to their wallet address server-side.
**ChatMessage fields** (each item in `messages`): `id` (`string`), `role` (`'user' | 'assistant'`), `content` (`string`), `isStreaming` (`boolean`), `statusLine` (`string | undefined` — live status text during streaming), `toolCalls` (`ToolCall[]`), `visualizations` (`VisualizationData[]`), `searchResults` (`SearchResultsData[]`).
```ts
const { messages, isStreaming, isLoading, toolCalls, connectedDataSources, error, query, clearMessages, startNewSession, cancelQuery, interruptAgent, respondToInterrupt } = useAgent(template?, options?)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `template` | `'data-science' | 'marketing' | 'support' | 'custom'` | — | Fallback template label (first arg). For persona-backed apps the registered persona overrides it — use `'custom'` (or omit) and pick the brain via `persona`. The template does NOT select the agent. |
| `options.capabilities` | `string[]` | — | Pre-load tool categories the agent may use. The nine categories are `data_access`, `external_integration`, `site_operations`, `code_execution`, `domain_task`, `workspace_management`, `agent_management`, `specialist_agents`, `automation_management` — `data_access` covers the MongoDB read/write tools needed for the Reason→Write→Read pattern. Grant only what the task needs; most built-app agents only need `data_access`. For persona-backed apps the registered definition is authoritative — capabilities passed here are advisory and cannot widen it. Run `casino_list_tools` for the live category set. |
| `options.persona` | `string` | since 0.2.1 | Target a specific registered persona/subagent by name (maps to `target_agents`). Without it the backend auto-selects the FIRST registered persona, so multi-persona apps MUST set this to reach the others. (`agentName` is an alias.) |
| `options.systemPrompt` | `string` | since 0.2.1 | Inline system-prompt override for this hook (maps to `system_prompt_override`). Use to tune behavior per-surface without registering a separate persona. |
| `options.sessionKey` | `string` | since 0.2.1 | Namespaces this hook's conversation. Pass a stable, distinct value per surface (e.g. `'chat'`, `'tasks'`) so independent `useAgent` instances don't share one conversation. It also persists that surface's history across reloads (same tab). Omit only for a single-surface app. |
| `options.tools` | `string[]` | — | Whitelist specific tool names (finer-grained than `capabilities`). |
| `options.targetAgent(s)` | `string | string[]` | — | Deprecated (Strands swarm, removed in P0-73). Use `persona` instead. |
### Returns
| Field | Type | Description |
|---|---|---|
| `messages` | `ChatMessage[]` | Conversation messages (see ChatMessage fields). |
| `isStreaming` | `boolean` | Whether the agent is currently streaming a response. |
| `isLoading` | `boolean` | Whether a query is in flight. |
| `toolCalls` | `ToolCall[]` | Active/completed tool executions. |
| `connectedDataSources` | `DataSourceBadgeInfo[]` | Connected data sources. |
| `error` | `string | null` | Last error message. |
| `query` | `(prompt: string, attachments?: AttachmentInput[], allowedTerms?: string[]) => Promise` | Send a prompt to the agent. Optionally pass `attachments` — images (png/jpeg/webp/gif) ride the /stream wire as vision content blocks the model can SEE (since 0.2.5); non-image files (PDF/CSV/DOCX…) upload before streaming and land as datasets/context. `allowedTerms` scopes PII unmasking for this turn. |
| `clearMessages` | `() => void` | Wipe in-memory messages and clear the stored session ID, so the next `query()` starts a fresh session. |
| `startNewSession` | `() => void` | Force a brand-new backend session (fresh conversation), discarding prior context. Stronger than `clearMessages()`, which only clears the local session id. |
| `cancelQuery` | `() => void` | Cancel the in-flight query. |
| `interruptAgent` | `() => Promise` | Pause the agent mid-execution. |
| `respondToInterrupt` | `(response: string) => Promise` | Resume a paused agent with user input. |
### Examples
**Persona-backed, multi-surface app**
```tsx
// Each surface gets its own brain + isolated conversation.
const chat = useAgent('custom', { capabilities: ['data_access'], persona: 'assistant', sessionKey: 'chat' });
const worker = useAgent('custom', { capabilities: ['data_access'], persona: 'worker', sessionKey: 'tasks' });
```
**Multi-turn chat with automatic memory**
```tsx
function AssistantChat() {
const { query, messages, isStreaming } = useAgent(undefined, {
persona: 'assistant' // agent registered via casino_create_agent
});
// Turn 1: "summarize this dataset"
// Turn 2: "now group it by region" ← agent remembers "this dataset"
// Turn 3: "which region is the biggest?" ← agent knows the grouping from turn 2
return (
);
}
```
**Persist a conversation across reloads (0.2.1+)**
```tsx
const chat = useAgent('custom', { capabilities: ['data_access'], sessionKey: 'support-chat' });
// Reopening/reloading this surface resumes the same conversation; a different sessionKey is a
// different conversation. Call chat.clearMessages() / startNewSession() to start fresh.
```
> ⚠️ **Warning:** Multiple `useAgent` instances on one page: before 0.2.1 they all SHARED one tenant-wide session, so surfaces bled into each other (a reply from one surface showing up in another). Fix: give each instance a distinct `sessionKey` (0.2.1+) so conversations stay isolated.
> ℹ️ **Note:** If a built app appears to "forget" every turn: (1) check your SDK version — apps built before the `sessionIdRef` fix don't pass a session ID at all, so rebuild via `edit_site`/`build_site`; (2) make sure you're not remounting the component between queries (conditional rendering on `isStreaming` can cause remounts); (3) ensure multiple instances each use a distinct `sessionKey`; (4) check the console for warnings about `crypto.randomUUID` not being available (very old browsers).
> ℹ️ **Note:** Use `useConversations()` to list the user's past conversations and `useConversation(id)` to restore one (both shipped). For custom multi-conversation UIs you can also manage session IDs at the application level.
> ℹ️ **Note:** **Image attachments (0.2.5+):** pass `File`s (or `{ filename, content_type, data }` wire objects) as the 2nd arg to `query`. Images with a vision-capable mime type (`image/png`, `image/jpeg`, `image/webp`, `image/gif`) are sent inline as base64 vision blocks on the `/stream` request, so the model actually sees them. Non-image files continue to upload first and attach as datasets/context.
**See also:** [useCollection](https://flowstack.fun/docs/useCollection) · [useAuth](https://flowstack.fun/docs/useAuth) · [useDatasets](https://flowstack.fun/docs/useDatasets)
*Source: README.md#useagent*
## useAgents
*hook* — Discover the agents available to the current site, including their name, description, tools, trigger phrases, and intended use.
```ts
import { useAgents } from 'flowstack-sdk'
```
```ts
const { agents, isLoading, refreshAgents } = useAgents()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `agents` | `AgentInfo[]` | Available agents — each has `name`, `description`, `tools`, `triggerPhrases`, and `useFor`. |
| `isLoading` | `boolean` | Fetch in flight. |
| `refreshAgents` | `() => Promise` | Manual re-fetch of the agent list. |
### Examples
```tsx
const { agents, isLoading, refreshAgents } = useAgents();
// agents: AgentInfo[] — each has name, description, tools, triggerPhrases, useFor
```
**See also:** [useAgent](https://flowstack.fun/docs/useAgent) · [targeting-custom-agents](https://flowstack.fun/docs/targeting-custom-agents) · [agent-catalog](https://flowstack.fun/docs/agent-catalog)
*Source: README.md#useagents-discovery*
## useConversations
*hook · ⚠ not for built apps* — Fetch the user's past Casino builder conversations.
```ts
import { useConversations } from 'flowstack-sdk'
```
Fetches the user's past Casino builder conversations from `GET /library/conversations`. Exported from `flowstack-sdk` and sharing the same `FlowstackProvider` context.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useConversations.ts` as the source of truth.
**See also:** [useAgent](https://flowstack.fun/docs/useAgent) · [useDataOverview](https://flowstack.fun/docs/useDataOverview)
*Source: README.md#additional-hooks-05*
## useIntentAgent
*hook* — Create and manage agents dynamically based on user intent.
```ts
import { useIntentAgent } from 'flowstack-sdk'
```
Creates and manages agents dynamically based on user intent. Exported from `flowstack-sdk` and sharing the same `FlowstackProvider` context.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useIntentAgent.ts` as the source of truth.
**See also:** [useAgent](https://flowstack.fun/docs/useAgent) · [useAgents](https://flowstack.fun/docs/useAgents)
*Source: README.md#additional-hooks-05*
## useModels
*hook · ⚠ not for built apps* — ML model management — list registered models with their framework, metrics, and creation metadata.
```ts
import { useModels } from 'flowstack-sdk'
```
```ts
const { models, isLoading, error, refreshModels } = useModels()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `models` | `ModelInfo[]` | Registered models — each `{ name, framework, metrics, createdAt }`. |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `refreshModels` | `() => Promise` | Manual re-fetch of the model list. |
### Examples
```tsx
const {
models, // ModelInfo[] — { name, framework, metrics, createdAt }
isLoading, // boolean
error, // string | null
refreshModels, // () => Promise
} = useModels();
```
*Source: README.md#usemodels*
## useOllamaDetection
*hook · ⚠ not for built apps* — Detect a local Ollama instance and list its available models.
```ts
import { useOllamaDetection } from 'flowstack-sdk'
```
Detect a local Ollama instance and list available models. Exported from `flowstack-sdk` and sharing the same `FlowstackProvider` context.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useOllamaDetection.ts` as the source of truth.
**See also:** [useProviderCredentials](https://flowstack.fun/docs/useProviderCredentials) · [useModels](https://flowstack.fun/docs/useModels)
*Source: README.md#additional-hooks-05*
## useProviderCredentials
*hook · ⚠ not for built apps* — Manage LLM provider credentials (BYOK and Ollama).
```ts
import { useProviderCredentials } from 'flowstack-sdk'
```
Manage LLM provider credentials (bring-your-own-key plus Ollama). Exported from `flowstack-sdk` and sharing the same `FlowstackProvider` context.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useProviderCredentials.ts` as the source of truth.
**See also:** [useOllamaDetection](https://flowstack.fun/docs/useOllamaDetection) · [useModels](https://flowstack.fun/docs/useModels)
*Source: README.md#additional-hooks-05*
## useToolInvocation
*hook · ⚠ not for built apps* — Low-level hook for directly invoking an agent tool.
```ts
import { useToolInvocation } from 'flowstack-sdk'
```
Direct tool invocation hook. Exported from `flowstack-sdk` and sharing the same `FlowstackProvider` context.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useToolInvocation.ts` as the source of truth.
> ⚠️ **Warning:** Built apps should **not** use `useToolInvocation` to read data. Read all data with `useCollection` and let writes flow through deterministic actions or the agent's `data_access` tools (Reason → Write → Read).
**See also:** [useCollection](https://flowstack.fun/docs/useCollection) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#additional-hooks-05*
---
# Workspaces & Users
Multi-tenant workspaces and user management.
## useSiteVersions
*hook · ⚠ not for built apps* — Work with the version history of a published site.
```ts
import { useSiteVersions } from 'flowstack-sdk'
```
Hook for working with site versions. The README lists this hook without a detailed signature — see the SDK source for full usage.
Treat the type signatures in `packages/flowstack-sdk/src/hooks/useSiteVersions.ts` as the source of truth.
**See also:** [useSites](https://flowstack.fun/docs/useSites)
*Source: README.md#additional-hooks-05*
## useUserManagement
*hook · ⚠ not for built apps* — Admin hook to list, search, paginate, and mutate managed users plus view user stats and activity.
```ts
import { useUserManagement } from 'flowstack-sdk'
```
Admin user management. **Role hierarchy:** `owner` > `admin` > `member` > `viewer`.
```ts
const { users, stats, isLoading, error, canManageUsers, pagination, refreshUsers, getUser, updateUser, suspendUser, reactivateUser, deleteUser, getUserActivity, refreshStats, setPage, setSearch, setRoleFilter, setStatusFilter } = useUserManagement()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `users` | `ManagedUser[]` | The managed users. |
| `stats` | `UserStats | null` | { totalUsers, activeUsers, newUsersThisMonth }. |
| `isLoading` | `boolean` | Request in flight. |
| `error` | `string | null` | Last error message. |
| `canManageUsers` | `boolean` | Whether the current user has management permission. |
| `pagination` | `{ page, limit, totalCount, hasMore }` | Pagination state. |
| `refreshUsers` | `(params?) => Promise` | Re-fetch the user list. |
| `getUser` | `(userId) => Promise` | Fetch a single user. |
| `updateUser` | `(userId, updates) => Promise` | Update a user. |
| `suspendUser` | `(userId, reason?) => Promise` | Suspend a user. |
| `reactivateUser` | `(userId) => Promise` | Reactivate a suspended user. |
| `deleteUser` | `(userId) => Promise` | Delete a user. |
| `getUserActivity` | `(userId, limit?) => Promise` | Fetch a user's activity log. |
| `refreshStats` | `() => Promise` | Re-fetch user stats. |
| `setPage` | `(page) => void` | Set the current page. |
| `setSearch` | `(search) => void` | Set the search query. |
| `setRoleFilter` | `(role) => void` | Filter by role. |
| `setStatusFilter` | `(status) => void` | Filter by status. |
### Examples
**Admin user management**
```tsx
const {
users, // ManagedUser[]
stats, // UserStats | null — { totalUsers, activeUsers, newUsersThisMonth }
isLoading, // boolean
error, // string | null
canManageUsers, // boolean
pagination, // { page, limit, totalCount, hasMore }
refreshUsers, // (params?) => Promise
getUser, // (userId) => Promise
updateUser, // (userId, updates) => Promise
suspendUser, // (userId, reason?) => Promise
reactivateUser, // (userId) => Promise
deleteUser, // (userId) => Promise
getUserActivity, // (userId, limit?) => Promise
refreshStats, // () => Promise
setPage, // (page) => void
setSearch, // (search) => void
setRoleFilter, // (role) => void
setStatusFilter, // (status) => void
} = useUserManagement();
```
> ℹ️ **Note:** Role hierarchy: `owner` > `admin` > `member` > `viewer`.
**See also:** [useWorkspace](https://flowstack.fun/docs/useWorkspace)
*Source: README.md#useusermanagement*
## useWorkspace
*hook · ⚠ not for built apps* — List, create, and select workspaces for the current user.
```ts
import { useWorkspace } from 'flowstack-sdk'
```
```ts
const { workspaces, selectedWorkspace, isLoading, error, createWorkspace, selectWorkspace, refreshWorkspaces } = useWorkspace()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `workspaces` | `WorkspaceInfo[]` | Available workspaces. |
| `selectedWorkspace` | `WorkspaceInfo | null` | The currently selected workspace. |
| `isLoading` | `boolean` | Request in flight. |
| `error` | `string | null` | Last error message. |
| `createWorkspace` | `(name, description?) => Promise` | Create a new workspace. |
| `selectWorkspace` | `(workspace) => void` | Set the active workspace. |
| `refreshWorkspaces` | `() => Promise` | Re-fetch the workspace list. |
### Examples
**Workspace management**
```tsx
const {
workspaces, // WorkspaceInfo[]
selectedWorkspace, // WorkspaceInfo | null
isLoading, // boolean
error, // string | null
createWorkspace, // (name, description?) => Promise
selectWorkspace, // (workspace) => void
refreshWorkspaces, // () => Promise
} = useWorkspace();
```
**See also:** [useUserManagement](https://flowstack.fun/docs/useUserManagement)
*Source: README.md#useworkspace*
---
# Components
Pre-built pages, forms, and rendering components.
## AuthPage
*component · ⚠ not for built apps* — Complete pre-built auth page with login/register tabs and optional Google sign-in.
```ts
import { AuthPage } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `defaultTab` | `"login" | "register"` | — | Which tab is active on mount. |
| `onSuccess` | `() => void` | — | Called after a successful auth, e.g. to route to the dashboard. |
| `showGoogle` | `boolean` | — | Show the Google sign-in button. |
### Examples
**Complete auth page with login/register tabs**
```tsx
router.push('/dashboard')} showGoogle />
```
**See also:** [LoginForm](https://flowstack.fun/docs/LoginForm) · [RegisterForm](https://flowstack.fun/docs/RegisterForm) · [AuthGuard](https://flowstack.fun/docs/AuthGuard)
*Source: README.md#pre-built-page-components*
## ChatInterface
*component* — Embeddable chat interface bound to an agent template.
```ts
import { ChatInterface } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `template` | `string` | — | Agent template to use, e.g. `data-science`. |
| `height` | `number` | — | Interface height in pixels. |
| `placeholder` | `string` | — | Input placeholder text. |
### Examples
**Chat interface**
```tsx
```
**See also:** [ChatPage](https://flowstack.fun/docs/ChatPage) · [MessageList](https://flowstack.fun/docs/MessageList) · [MarkdownRenderer](https://flowstack.fun/docs/MarkdownRenderer) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#workspace--data-components*
## ChatPage
*component* — Full pre-built chat interface page.
```ts
import { ChatPage } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `title` | `string` | — | Chat page title. |
| `placeholder` | `string` | — | Input placeholder text. |
| `welcomeMessage` | `React.ReactNode` | — | Welcome content shown before the conversation starts. |
### Examples
**Full chat interface**
```tsx
} />
```
**See also:** [ChatInterface](https://flowstack.fun/docs/ChatInterface) · [MessageList](https://flowstack.fun/docs/MessageList) · [MarkdownRenderer](https://flowstack.fun/docs/MarkdownRenderer)
*Source: README.md#pre-built-page-components*
## CreateWorkspaceModal
*component · ⚠ not for built apps* — Modal dialog for creating a new workspace.
```ts
import { CreateWorkspaceModal } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `isOpen` | `boolean` | — | Whether the modal is open. |
| `onCreated` | `(workspace) => void` | — | Called after a workspace is created. |
### Examples
**Create workspace modal**
```tsx
```
**See also:** [WorkspaceSelector](https://flowstack.fun/docs/WorkspaceSelector)
*Source: README.md#workspace--data-components*
## DashboardLayout
*component · ⚠ not for built apps* — Pre-built dashboard layout with a sidebar, header, and content area.
```ts
import { DashboardLayout } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `sidebar` | `React.ReactNode` | — | Sidebar content. |
| `header` | `React.ReactNode` | — | Header content. |
| `children` | `React.ReactNode` | — | Main content area. |
### Examples
**Dashboard layout with sidebar**
```tsx
} header={}>
```
**See also:** [AuthPage](https://flowstack.fun/docs/AuthPage) · [ChatPage](https://flowstack.fun/docs/ChatPage)
*Source: README.md#pre-built-page-components*
## DatasetUploader
*component · ⚠ not for built apps* — File-upload component for ingesting datasets such as CSV, XLSX, and Parquet files.
```ts
import { DatasetUploader } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `onUpload` | `(file) => void` | — | Called when a dataset is uploaded. |
| `accept` | `string` | — | Accepted file types, e.g. `.csv,.xlsx,.parquet`. |
### Examples
**Dataset uploader**
```tsx
```
**See also:** [useDatasets](https://flowstack.fun/docs/useDatasets)
*Source: README.md#workspace--data-components*
## LoginForm
*component · ⚠ not for built apps* — Pre-built login form with success/error callbacks and an optional register link.
```ts
import { LoginForm } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `onSuccess` | `() => void` | — | Called on successful login. |
| `onError` | `(error: unknown) => void` | — | Called when login fails. |
| `showRegisterLink` | `boolean` | — | Show a link to the register form. |
### Examples
**Login form**
```tsx
```
**See also:** [RegisterForm](https://flowstack.fun/docs/RegisterForm) · [AuthGuard](https://flowstack.fun/docs/AuthGuard) · [AuthPage](https://flowstack.fun/docs/AuthPage)
*Source: README.md#form-components*
## MarkdownRenderer
*component* — Drop-in component for rendering agent responses with full GFM support and automatic mid-stream table repair.
```ts
import { MarkdownRenderer } from 'flowstack-sdk'
```
Drop-in component for rendering agent responses with full GFM support (tables, code blocks, inline formatting). Handles mid-stream table repair automatically — no Tailwind or external CSS required.
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `content` | `string` | required | Raw markdown from `message.content`. |
| `isStreaming` | `boolean` | — | Pass `true` while streaming to apply mid-stream table repair. |
| `className` | `string` | — | Custom class for the wrapper `
);
}
```
**See also:** [useAgent](https://flowstack.fun/docs/useAgent) · [MessageList](https://flowstack.fun/docs/MessageList) · [ChatInterface](https://flowstack.fun/docs/ChatInterface)
*Source: README.md#markdownrenderer*
## MessageList
*component* — Renders a list of chat messages, with optional streaming state.
```ts
import { MessageList } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `messages` | `Message[]` | — | The messages to render. |
| `isStreaming` | `boolean` | — | Whether a response is currently streaming. |
### Examples
**Message list**
```tsx
```
**See also:** [ChatInterface](https://flowstack.fun/docs/ChatInterface) · [MarkdownRenderer](https://flowstack.fun/docs/MarkdownRenderer) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#workspace--data-components*
## RegisterForm
*component · ⚠ not for built apps* — Pre-built registration form with a success callback and an optional login link.
```ts
import { RegisterForm } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `onSuccess` | `() => void` | — | Called on successful registration. |
| `showLoginLink` | `boolean` | — | Show a link to the login form. |
### Examples
**Register form**
```tsx
```
**See also:** [LoginForm](https://flowstack.fun/docs/LoginForm) · [AuthGuard](https://flowstack.fun/docs/AuthGuard) · [AuthPage](https://flowstack.fun/docs/AuthPage)
*Source: README.md#form-components*
## WorkspaceSelector
*component · ⚠ not for built apps* — Pre-built control for selecting the active workspace.
```ts
import { WorkspaceSelector } from 'flowstack-sdk'
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `onSelect` | `(workspace) => void` | — | Called when a workspace is selected. |
### Examples
**Workspace selector**
```tsx
```
**See also:** [CreateWorkspaceModal](https://flowstack.fun/docs/CreateWorkspaceModal)
*Source: README.md#workspace--data-components*
---
# Wallet
The wallet module: balances, transactions, and payment components.
## Wallet Module
*guide · ⚠ not for built apps* — Separate `flowstack-sdk/wallet` entry point for the two token systems (AGENT build credits, INFER query credits): reading balances, accepting payments, and accessing wallet addresses.
```ts
import { useWalletAuth, useInferBalance, useAgentBalance, useDeposit, useBuyInfer } from 'flowstack-sdk/wallet'
```
The wallet module lives at its own entry point, `flowstack-sdk/wallet`, and is imported only when an app needs INFER token payments. It requires peer dependencies (`@privy-io/react-auth`, `wagmi`, `viem`, `@tanstack/react-query`) and needs Privy running on the page's origin — which is only possible on `casino.flowstack.fun`, not on `*.casino.flowstack.fun` built-app subdomains.
**Built apps must NOT use this module for authentication** — use `BrokeredLoginButton` from `flowstack-sdk` instead. Use the wallet module only when your app needs to read/display INFER token balances, accept token payments, or access wallet addresses directly.
**Tokens & credits.** Flowstack has two token systems:
- **INFER** — *query/inference* credits. Read via `useInferBalance()` → `{ balance, available, queryCredits }` (`GET /billing/infer/balance`, on-chain). Spent on agent queries/inference. Top up with `useBuyInfer()` (fiat on-ramp) / `BuyInferModal` / `InferBalanceBadge`.
- **AGENT** — *build/compute* credits. Read via `useAgentBalance()` → `{ balance, available, buildCredits }` (`GET /billing/agent/balance`). Spent on builds/compute. Surfaced by `AgentBalanceBadge`; `NeedsAgent` gates content behind a balance and links to the OIF `/buy` on-ramp.
Rule of thumb: **INFER = `queryCredits` (asking the agent things)**, **AGENT = `buildCredits` (building/running compute)**. They are separate balances with separate endpoints and on-ramps.
### Examples
**Required peer dependencies**
```json
{
"dependencies": {
"@privy-io/react-auth": ">=2.0.0",
"wagmi": ">=2.0.0",
"viem": ">=2.0.0",
"@tanstack/react-query": ">=5.0.0"
}
}
```
> ⚠️ **Warning:** NOT for built apps: this entry statically imports the `wagmi` optional peer dependency. Built apps that only need monetization gating import `AppPaywall`/`useAppAccess` from `flowstack-sdk/paywall` (0.3.2+) instead.
> ⚠️ **Warning:** **Built apps: do not use this module for authentication.** The wallet module needs Privy running on the page's origin, which is only possible on `casino.flowstack.fun`, not on `*.casino.flowstack.fun` built-app subdomains. **Use `BrokeredLoginButton` for auth in built apps.**
**See also:** [useWalletAuth](https://flowstack.fun/docs/useWalletAuth) · [useInferBalance](https://flowstack.fun/docs/useInferBalance) · [useAgentBalance](https://flowstack.fun/docs/useAgentBalance) · [useDeposit](https://flowstack.fun/docs/useDeposit) · [useBuyInfer](https://flowstack.fun/docs/useBuyInfer) · [BrokeredLoginButton](https://flowstack.fun/docs/BrokeredLoginButton)
*Source: README.md#wallet-module*
## paywall-app
*recipe · since 0.2.8* — Charge users of the app for access (unlock fee or metered queries) using the platform paywall
**Plane:** monetization
**Steps:**
1. `casino_set_monetization` — Owner enables monetization on the site (pricing mode + amounts) — this is platform-level config, not app code.
2. `AppPaywall` — Wrap the app content in — renders the unlock CTA when access is denied.
3. `useAppAccess` — Use directly only for custom gate UI (hasAccess, queriesRemaining, checkout); AppPaywall already handles the default.
### Examples
**End-to-end example**
```tsx
import { AppPaywall } from '@flowstack/sdk/paywall';
export default function App() {
return (
);
}
```
> ⚠️ **Warning:** Import from '@flowstack/sdk/paywall' (0.3.2+), NOT '/wallet' — the wallet entry statically imports the wagmi peer dep and fails built-app builds.
> ⚠️ **Warning:** Monetizing the app = platform paywall. Do NOT wire raw Stripe calls to charge app users (that is the call-stripe-api anti-case).
> ⚠️ **Warning:** The backend gate enforces access regardless — AppPaywall is the UI surface, never the security boundary.
> ⚠️ **Warning:** The app_paywall SSE event fires mid-conversation when metered credits run out; AppPaywall listens for it automatically.
**See also:** [call-stripe-api](https://flowstack.fun/docs/call-stripe-api) · [casino_set_monetization](https://flowstack.fun/docs/casino_set_monetization) · [AppPaywall](https://flowstack.fun/docs/AppPaywall) · [useAppAccess](https://flowstack.fun/docs/useAppAccess)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## useAgentBalance
*hook* — Poll the connected wallet's AGENT token balance so built apps can check whether the end-user can afford an operation.
```ts
import { useAgentBalance } from 'flowstack-sdk/wallet'
```
Polls `GET /billing/agent/balance` for the connected wallet's AGENT token balance. For built apps that need to check whether the end-user has enough AGENT to run an operation.
Use `data.available` for spendable AGENT (balance minus active holds) and `data.balance` for total AGENT in the wallet.
```ts
const { data, isLoading, error, refetch } = useAgentBalance()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `data` | `AgentBalance | null` | Balance object: `{ balance, available, buildCredits, balanceWei, ... }`. |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `refetch` | `() => Promise` | Manually re-fetch the balance. |
### Examples
**Reading available vs total AGENT**
```tsx
const { data } = useAgentBalance();
// data.available — spendable AGENT (balance minus active holds)
// data.balance — total AGENT in wallet
```
**See also:** [useInferBalance](https://flowstack.fun/docs/useInferBalance) · [NeedsAgent](https://flowstack.fun/docs/NeedsAgent) · [AgentBalanceBadge](https://flowstack.fun/docs/AgentBalanceBadge)
*Source: README.md#useagentbalance*
## useAppAccess
*hook · since 0.2.4* — Check whether the authenticated end-user has access to a monetized built app, and open Stripe Checkout to unlock it. Use directly only for custom gate UI — `AppPaywall` wraps this with a default overlay.
```ts
import { useAppAccess } from 'flowstack-sdk/paywall'
```
Polls `GET /billing/app-access/status` every 60 seconds and exposes the access state plus a `checkout()` function that opens a Stripe Checkout session for a one-time unlock. 0.3.0 adds per-app subscriptions (P0-162): a `subscription` status block plus `subscribe()` / `cancelSubscription()` for the builder's monthly plan. Fails open on non-200 responses so a billing outage never locks users out client-side — the backend gate remains the enforcement boundary regardless of what this hook reports.
Most apps should render `` instead of consuming this hook directly; reach for `useAppAccess` when you need a custom paywall surface (pricing copy, metered-usage meter, custom CTA).
```ts
const { hasAccess, queriesUsed, queriesRemaining, paymentMode, unlockPriceCents, unlockPriceLabel, isLoading, error, checkout, refetch } = useAppAccess(siteId, opts?)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `siteId` | `string` | required | Site ID of the built app (typically config.appScope). |
| `opts.builderTenantId` | `string` | — | Builder's tenant ID — needed to load app config when the viewer belongs to a different tenant. |
### Returns
| Field | Type | Description |
|---|---|---|
| `hasAccess` | `boolean` | Whether the end-user has access to this built app (optimistic true until the first check resolves). |
| `queriesUsed` | `number` | Free queries used this month. |
| `queriesRemaining` | `number | null` | Remaining free queries this month (null = unlimited / paid access). |
| `paymentMode` | `'stripe' | 'agent' | 'both' | null` | Payment mode configured by the builder. |
| `unlockPriceCents` | `number | null` | One-time unlock price in USD cents (null = free or not configured). |
| `unlockPriceLabel` | `string | null` | Human-readable unlock price label. |
| `subscription` | `AppSubscriptionStatus | null` | The end-user's subscription to this app (state, currentPeriodEnd, cancelAtPeriodEnd, label) — status-only since 0.3.3/P0-167, no allowance fields. null when the builder has no subscription plan configured. |
| `isLoading` | `boolean` | Access check in flight. |
| `error` | `string | null` | Error message if the status check failed. |
| `checkout` | `(opts?: { successUrl?: string; cancelUrl?: string }) => Promise` | Open Stripe Checkout to unlock this app. |
| `subscribe` | `(opts?: { successUrl?: string; cancelUrl?: string }) => Promise` | Open Stripe Checkout (mode=subscription) for the builder's monthly plan. 409s if already subscribed or the user owns a lifetime unlock. |
| `cancelSubscription` | `() => Promise` | Cancel at period end — access and allowance continue until currentPeriodEnd. |
| `refetch` | `() => Promise` | Manually refresh the access status. |
### Examples
**Custom gate UI**
```tsx
import { useAppAccess } from 'flowstack-sdk/paywall';
function Gate({ children }: { children: React.ReactNode }) {
const { hasAccess, queriesRemaining, unlockPriceLabel, isLoading, checkout } = useAppAccess(config.appScope);
if (isLoading) return
Checking access…
;
if (hasAccess) return <>{children}>;
return (
{queriesRemaining === 0 ? 'Free queries used up.' : 'This app is premium.'}
);
}
```
> ⚠️ **Warning:** Import from `flowstack-sdk/paywall` (0.3.2+), not `flowstack-sdk/wallet` — the wallet entry statically requires the `wagmi` optional peer and breaks built-app builds.
> 🚫 **Critical:** The backend AppAccessGate enforces access regardless — this hook (and `AppPaywall`) is UI, never the security boundary.
> ℹ️ **Note:** To monetize an app, the owner configures pricing via `setMonetization` (or the Casino Monetization panel); this hook only reads the resulting access state.
**See also:** [AppPaywall](https://flowstack.fun/docs/AppPaywall) · [setMonetization](https://flowstack.fun/docs/setMonetization) · [getMonetization](https://flowstack.fun/docs/getMonetization) · [PaymentRequired](https://flowstack.fun/docs/PaymentRequired) · [AppSubscriptionStatus](https://flowstack.fun/docs/AppSubscriptionStatus) · [subscribeToApp](https://flowstack.fun/docs/subscribeToApp) · [cancelAppSubscription](https://flowstack.fun/docs/cancelAppSubscription)
*Source: wallet/useAppAccess.ts*
## useBuyInfer
*hook · ⚠ not for built apps* — Open the fiat on-ramp widget to buy INFER tokens and track purchase status.
```ts
import { useBuyInfer } from 'flowstack-sdk/wallet'
```
Opens a fiat on-ramp widget so the user can purchase INFER tokens, exposing the in-progress purchase status. Part of the `flowstack-sdk/wallet` entry point.
```ts
const { buy, isBuying, status, error } = useBuyInfer()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `buy` | `(amount?: number) => void` | Opens the fiat on-ramp widget. |
| `isBuying` | `boolean` | Purchase in progress. |
| `status` | `'idle' | 'pending' | 'completed' | 'failed'` | Current purchase status. |
| `error` | `string | null` | Last error message. |
**See also:** [useDeposit](https://flowstack.fun/docs/useDeposit) · [useInferBalance](https://flowstack.fun/docs/useInferBalance) · [BuyInferModal](https://flowstack.fun/docs/BuyInferModal)
*Source: README.md#usebuyinfer*
## useDeposit
*hook · ⚠ not for built apps* — Deposit INFER tokens from the connected wallet and track the resulting transaction hash.
```ts
import { useDeposit } from 'flowstack-sdk/wallet'
```
Deposits a token amount from the connected wallet, returning the transaction hash. Part of the `flowstack-sdk/wallet` entry point.
```ts
const { deposit, isDepositing, txHash, error } = useDeposit()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `deposit` | `(amount: number) => Promise` | Deposit the given amount; resolves to the tx hash. |
| `isDepositing` | `boolean` | Deposit transaction in flight. |
| `txHash` | `string | null` | Hash of the deposit transaction. |
| `error` | `string | null` | Last error message. |
**See also:** [useInferBalance](https://flowstack.fun/docs/useInferBalance) · [useBuyInfer](https://flowstack.fun/docs/useBuyInfer)
*Source: README.md#usedeposit*
## useInferBalance
*hook · ⚠ not for built apps* — Read the connected wallet's INFER token balance, including available balance and query credits.
```ts
import { useInferBalance } from 'flowstack-sdk/wallet'
```
Returns the connected wallet's INFER token balance for display. Part of the `flowstack-sdk/wallet` entry point.
```ts
const { data, isLoading, error, refetch } = useInferBalance()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `data` | `InferBalance | null` | Balance object: `{ balance, available, queryCredits, balanceWei, heldWei, availableWei }`. |
| `isLoading` | `boolean` | Fetch in flight. |
| `error` | `string | null` | Last error message. |
| `refetch` | `() => Promise` | Manually re-fetch the balance. |
**See also:** [useAgentBalance](https://flowstack.fun/docs/useAgentBalance) · [useDeposit](https://flowstack.fun/docs/useDeposit) · [useBuyInfer](https://flowstack.fun/docs/useBuyInfer) · [InferBalanceBadge](https://flowstack.fun/docs/InferBalanceBadge)
*Source: README.md#useinferbalance*
## useWalletAuth
*hook · ⚠ not for built apps* — Read wallet connection state and trigger Privy/SIWE login or logout from the `flowstack-sdk/wallet` entry point.
```ts
import { useWalletAuth } from 'flowstack-sdk/wallet'
```
Provides wallet connection state and login/logout controls for the wallet module. Available only on `casino.flowstack.fun` where Privy can run on the page origin. Built apps authenticate via `BrokeredLoginButton` instead.
```ts
const { isConnected, isLoading, address, isEmbeddedWallet, authMethod, error, login, logout } = useWalletAuth()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `isConnected` | `boolean` | Whether a wallet is connected. |
| `isLoading` | `boolean` | Auth request in flight. |
| `address` | `string | null` | Checksummed wallet address. |
| `isEmbeddedWallet` | `boolean` | Privy embedded wallet vs MetaMask. |
| `authMethod` | `'privy' | 'siwe' | null` | The active authentication method. |
| `error` | `string | null` | Last auth error message. |
| `login` | `(method?: 'privy' | 'wallet') => Promise` | Connect via Privy or an external wallet. |
| `logout` | `() => void` | Disconnect the wallet. |
> ⚠️ **Warning:** **Built apps: do not use this hook for authentication.** Use `BrokeredLoginButton` from `flowstack-sdk` instead — Privy can only run on `casino.flowstack.fun`, not on built-app subdomains.
**See also:** [useInferBalance](https://flowstack.fun/docs/useInferBalance) · [useAgentBalance](https://flowstack.fun/docs/useAgentBalance) · [LoginButton](https://flowstack.fun/docs/LoginButton) · [BrokeredLoginButton](https://flowstack.fun/docs/BrokeredLoginButton)
*Source: README.md#usewalletauth*
## AgentBalanceBadge
*component · ⚠ not for built apps* — Display badge showing the connected wallet's AGENT token balance.
```ts
import { AgentBalanceBadge } from 'flowstack-sdk/wallet'
```
Renders a badge showing the connected wallet's AGENT token balance. Part of the `flowstack-sdk/wallet` entry point.
```ts
```
### Examples
**Display AGENT balance**
```tsx
```
**See also:** [useAgentBalance](https://flowstack.fun/docs/useAgentBalance) · [InferBalanceBadge](https://flowstack.fun/docs/InferBalanceBadge)
*Source: README.md#components*
## AppPaywall
*component · since 0.2.4* — Access gate for monetized built apps: renders children when the end-user has access, otherwise shows a paywall overlay with the unlock CTA. The default way to charge users of an app.
```ts
import { AppPaywall } from 'flowstack-sdk/paywall'
```
Wraps app content and surfaces the platform paywall when `useAppAccess` reports the user is blocked, or immediately when the backend emits an `app_paywall` SSE event mid-conversation (metered credits running out). It does NOT replace backend enforcement — the backend's AppAccessGate always has the final say (402 / SSE event).
This is the monetization entry point for built apps: the owner sets pricing with `setMonetization` (platform-level config), the app wraps its content in `AppPaywall`, done. Do not wire raw Stripe calls to charge app users — the owner-registered Stripe integration is for operating the OWNER'S Stripe account, not for app monetization. 0.3.0 (P0-162): when the builder configures a monthly subscription plan, the paywall renders the plan CTA above the one-time unlock. Since 0.3.3 (P0-167) subscriptions are status-only: a subscriber with an open paid period has full access and never reaches this paywall, so the subscribe CTA shows only for lapsed or never-subscribed users.
```ts
...}>{children}
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `siteId` | `string` | required | Site ID of the built app (typically config.appScope). |
| `builderTenantId` | `string` | — | Builder's tenant ID — needed to load app config. |
| `children` | `React.ReactNode` | required | Content to show when the user has access. |
| `onBlocked` | `(reason: string) => void` | — | Called when the backend returns an app_paywall SSE event. |
### Examples
**Wrap the app**
```tsx
import { AppPaywall } from 'flowstack-sdk/paywall';
export default function App() {
return (
);
}
```
> ℹ️ **Note:** 0.3.3 (P0-167): when the backend blocks with reason `app_unfunded` (the app owner is out of compute credits), the paywall renders a "Temporarily unavailable" state with NO purchase CTAs — the end user can't fix owner funding, so don't offer them a buy button.
> ⚠️ **Warning:** Import from `flowstack-sdk/paywall` (0.3.2+). The `flowstack-sdk/wallet` entry statically imports the `wagmi` optional peer dependency (via useWalletAuth/useDeposit/WalletProvider) and fails to resolve in built apps that don't carry the wallet stack. `/wallet` still re-exports AppPaywall for consumers that do.
> 🚫 **Critical:** Backend enforcement is authoritative — AppPaywall is the UI surface, never the security boundary.
> ⚠️ **Warning:** Monetizing the app = platform paywall (`setMonetization` + this component). Do NOT use raw Stripe API calls to charge app users.
> ℹ️ **Note:** Listens for the `app_paywall` SSE event dispatched by `useAgent`, so metered apps gate mid-conversation without a page reload.
> ℹ️ **Note:** Since 0.3.1 (P0-164), the pay-with-AGENT option renders only when `payment_mode` is `'agent'` (token-only apps). In `'both'` mode the Stripe CTA is the single visible path — the AGENT rail still charges server-side; the paywall just no longer advertises it.
**See also:** [useAppAccess](https://flowstack.fun/docs/useAppAccess) · [setMonetization](https://flowstack.fun/docs/setMonetization) · [getMonetization](https://flowstack.fun/docs/getMonetization) · [PaymentRequired](https://flowstack.fun/docs/PaymentRequired) · [AppSubscriptionStatus](https://flowstack.fun/docs/AppSubscriptionStatus)
*Source: wallet/AppPaywall.tsx*
## BuyInferModal
*component · ⚠ not for built apps* — Modal for buying INFER tokens.
```ts
import { BuyInferModal } from 'flowstack-sdk/wallet'
```
Renders a modal for purchasing INFER tokens. Part of the `flowstack-sdk/wallet` entry point.
```ts
```
### Examples
**Buy INFER tokens modal**
```tsx
```
**See also:** [useBuyInfer](https://flowstack.fun/docs/useBuyInfer) · [PaymentRequired](https://flowstack.fun/docs/PaymentRequired) · [NeedsAgent](https://flowstack.fun/docs/NeedsAgent)
*Source: README.md#components*
## InferBalanceBadge
*component · ⚠ not for built apps* — Display badge showing the connected wallet's INFER token balance.
```ts
import { InferBalanceBadge } from 'flowstack-sdk/wallet'
```
Renders a badge showing the connected wallet's INFER token balance. Part of the `flowstack-sdk/wallet` entry point.
```ts
```
### Examples
**Display INFER balance**
```tsx
```
**See also:** [useInferBalance](https://flowstack.fun/docs/useInferBalance) · [AgentBalanceBadge](https://flowstack.fun/docs/AgentBalanceBadge)
*Source: README.md#components*
## LoginButton
*component · ⚠ not for built apps* — Privy/SIWE login button for the Casino platform only — built apps use `BrokeredLoginButton` instead.
```ts
import { LoginButton } from 'flowstack-sdk/wallet'
```
Renders a Privy/SIWE login button. Intended for the Casino PLATFORM only and NOT for built apps, which must use `BrokeredLoginButton` from `flowstack-sdk` because Privy can only run on `casino.flowstack.fun`.
```ts
```
### Examples
**Casino platform login**
```tsx
```
> ⚠️ **Warning:** Casino PLATFORM only — NOT for built apps. Built apps use `BrokeredLoginButton` from `flowstack-sdk` instead.
**See also:** [useWalletAuth](https://flowstack.fun/docs/useWalletAuth) · [BrokeredLoginButton](https://flowstack.fun/docs/BrokeredLoginButton)
*Source: README.md#components*
## NeedsAgent
*component* — Gate a feature behind a minimum AGENT balance, showing a deep-linked "Get AGENT →" CTA when the user has insufficient funds.
```ts
import { NeedsAgent } from 'flowstack-sdk/wallet'
```
Gates a feature behind a minimum AGENT balance. If the user has insufficient AGENT, it shows a "Get AGENT →" CTA that deep-links to the OIF `/buy` page. If sufficient, it renders children and calls `onProceed` when clicked.
**Deep-link flow:**
1. Built app renders ``
2. User has 0 AGENT → "Get AGENT →" button appears
3. Click opens `https://openinferencefoundation.org/buy?returnTo=&need=5`
4. User buys AGENT with a credit card on OIF (no wallet setup required — Privy creates it)
5. OIF redirects back to `returnUrl` — user now has AGENT and can proceed
```ts
{children}
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `amountNeeded` | `number` | required | Minimum AGENT balance required to proceed. |
| `onProceed` | `() => void` | — | Called when the user clicks through with sufficient balance. |
| `returnUrl` | `string` | — | URL the OIF /buy flow redirects back to after purchase, e.g. `window.location.href`. |
| `children` | `ReactNode` | — | Content rendered when the user has sufficient AGENT. |
### Examples
**Gate a feature behind 2 AGENT**
```tsx
Generate a new design — costs 2 AGENT
```
**See also:** [useAgentBalance](https://flowstack.fun/docs/useAgentBalance) · [PaymentRequired](https://flowstack.fun/docs/PaymentRequired) · [BuyInferModal](https://flowstack.fun/docs/BuyInferModal)
*Source: README.md#components*
## PaymentRequired
*component · ⚠ not for built apps* — Gate content behind payment, rendering children only once the requirement is met.
```ts
import { PaymentRequired } from 'flowstack-sdk/wallet'
```
Wraps content that should be gated behind payment. Part of the `flowstack-sdk/wallet` entry point.
```ts
{children}
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `children` | `ReactNode` | — | Content rendered once payment requirements are met. |
### Examples
**Gate content behind payment**
```tsx
```
**See also:** [NeedsAgent](https://flowstack.fun/docs/NeedsAgent) · [BuyInferModal](https://flowstack.fun/docs/BuyInferModal)
*Source: README.md#components*
## cancelAppSubscription
*utility · since 0.3.0* — Client function: cancel an app subscription at period end (P0-162).
```ts
import { cancelAppSubscription } from 'flowstack-sdk'
```
Non-React client function. Sets the subscription to cancel at period end — access and remaining allowance continue until `current_period_end` (standard SaaS behavior; immediate cancel is not offered). `POST /billing/app-access/subscription/cancel`. React apps should prefer `useAppAccess().cancelSubscription()`.
```ts
cancelAppSubscription(credentials, siteId, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The subscriber's credentials. |
| `siteId` | `string` | required | The app whose subscription to cancel. |
| `config` | `FlowstackClientConfig` | — | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | Confirmation with the period-end date access runs until. 404 if there is no active subscription. |
### Examples
**Cancel at period end**
```ts
await cancelAppSubscription(credentials, siteId, { appScope: siteId });
```
**See also:** [subscribeToApp](https://flowstack.fun/docs/subscribeToApp) · [useAppAccess](https://flowstack.fun/docs/useAppAccess) · [AppSubscriptionStatus](https://flowstack.fun/docs/AppSubscriptionStatus)
*Source: src/api/client.ts (App Subscriptions, P0-162)*
## getBuilderEarnings
*utility · since 0.2.9 · ⚠ not for built apps* — Client function: aggregate + per-app builder earnings across both rails (Stripe cents + on-chain AGENT).
```ts
import { getBuilderEarnings } from 'flowstack-sdk'
```
Non-React client function. Returns the builder's full earnings picture: legacy Stripe aggregate fields, per-rail breakdowns (`rails.stripe` in cents, `rails.agent` in wei/AGENT strings), a self-query summary (own-app usage excluded from earnings), and a per-app `apps[]` list with any required action (e.g. `set_payout_wallet`). `GET /billing/builder/earnings`.
```ts
getBuilderEarnings(credentials, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The builder's (app owner's) credentials. |
| `config` | `FlowstackClientConfig` | since 0.2.9 | Client config (baseUrl, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `BuilderEarnings` — aggregate fields, `rails`, `self_query`, and `apps: AppEarnings[]`. |
### Examples
**Show available payout**
```ts
const res = await getBuilderEarnings(credentials);
if (res.ok) {
const { available_cents, rails } = res.data;
console.log(`Fiat available: $${(available_cents / 100).toFixed(2)}`);
console.log(`AGENT accrued: ${rails.agent.accrued_agent}`); // human string — NOT wei
}
```
> 🚫 **Critical:** AGENT-rail amounts (`accrued_wei`, `pending_wei`, `paid_wei`, `release_wei`) are **strings** — wei values overflow JS numbers. Never `Number()`/`parseInt` them; display the paired human-decimal `*_agent` strings instead, and do math with BigInt if you must.
> ⚠️ **Warning:** Builder/owner operation — call with the **builder's** credentials from a dashboard/console context, not from inside a generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [requestBuilderPayout](https://flowstack.fun/docs/requestBuilderPayout) · [getConnectStatus](https://flowstack.fun/docs/getConnectStatus) · [BuilderEarnings](https://flowstack.fun/docs/BuilderEarnings) · [getMonetization](https://flowstack.fun/docs/getMonetization)
*Source: src/api/client.ts (Builder Earnings, P0-152)*
## getConnectStatus
*utility · since 0.2.9 · ⚠ not for built apps* — Client function: live Stripe Connect account status for the builder (refreshes cached flags).
```ts
import { getConnectStatus } from 'flowstack-sdk'
```
Non-React client function. Queries Stripe for the builder's Connect Express account state and refreshes the backend's cached flags. Use before showing payout UI: `payouts_enabled` gates `requestBuilderPayout`. `GET /billing/connect/status`.
```ts
getConnectStatus(credentials, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The builder's (app owner's) credentials. |
| `config` | `FlowstackClientConfig` | since 0.2.9 | Client config (baseUrl, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | `ConnectStatus`: `{ connected, stripe_account_id?, payouts_enabled, details_submitted? }`. |
### Examples
**Gate payout UI**
```ts
const res = await getConnectStatus(credentials);
const canPayout = res.ok && res.data.payouts_enabled;
```
> ⚠️ **Warning:** Builder/owner operation — call with the **builder's** credentials from a dashboard/console context, not from inside a generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [startConnectOnboarding](https://flowstack.fun/docs/startConnectOnboarding) · [requestBuilderPayout](https://flowstack.fun/docs/requestBuilderPayout) · [ConnectStatus](https://flowstack.fun/docs/ConnectStatus)
*Source: src/api/client.ts (Builder Earnings, P0-152)*
## getMonetization
*utility · since 0.2.6 · ⚠ not for built apps* — Client function: read a site's monetization (paywall) configuration.
```ts
import { getMonetization } from 'flowstack-sdk'
```
Non-React client function. `GET /billing/app-access/monetization/{siteId}`.
```ts
getMonetization(credentials, siteId, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `config` | `FlowstackClientConfig` | since 0.2.6 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | The stored `Monetization` config for the app. |
### Examples
**Read monetization**
```ts
const res = await getMonetization(credentials, siteId, { appScope });
if (res.ok) console.log(res.data.monetization.enabled);
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [setMonetization](https://flowstack.fun/docs/setMonetization) · [Monetization](https://flowstack.fun/docs/Monetization)
*Source: src/api/client.ts (App Monetization, P0-155)*
## requestBuilderPayout
*utility · since 0.2.9 · ⚠ not for built apps* — Client function: withdraw the builder's available fiat balance to their connected Stripe account.
```ts
import { requestBuilderPayout } from 'flowstack-sdk'
```
Non-React client function. Transfers the entire available fiat balance (fiat payouts drain the whole balance — no partial amounts) to the builder's Connect account. Requires `payouts_enabled` (see `getConnectStatus`) and `available_cents ≥ min_payout_cents` (see `getBuilderEarnings`). `POST /billing/builder/payout`.
```ts
requestBuilderPayout(credentials, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The builder's (app owner's) credentials. |
| `config` | `FlowstackClientConfig` | since 0.2.9 | Client config (baseUrl, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | The amount transferred and the Stripe transfer/payout ids. |
### Examples
**Withdraw**
```ts
const res = await requestBuilderPayout(credentials);
if (res.ok) console.log(`Paid $${(res.data.paid_cents / 100).toFixed(2)}`);
else console.error(res.error); // e.g. below minimum, payouts not enabled
```
> ⚠️ **Warning:** Fiat (Stripe-rail) only, and it drains the FULL available balance. AGENT-rail earnings settle on-chain to the payout wallet instead — see `BuilderEarnings.rails.agent` and the `set_payout_wallet` action.
> ⚠️ **Warning:** Builder/owner operation — call with the **builder's** credentials from a dashboard/console context, not from inside a generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [getBuilderEarnings](https://flowstack.fun/docs/getBuilderEarnings) · [getConnectStatus](https://flowstack.fun/docs/getConnectStatus) · [startConnectOnboarding](https://flowstack.fun/docs/startConnectOnboarding)
*Source: src/api/client.ts (Builder Earnings, P0-152)*
## setMonetization
*utility · since 0.2.6 · ⚠ not for built apps* — Client function: set a site's monetization (paywall) configuration.
```ts
import { setMonetization } from 'flowstack-sdk'
```
Non-React client function. Writes the paywall/pricing config for an app (Stripe and/or AGENT payment). `POST /billing/app-access/monetization/{siteId}`. 0.3.0 (P0-162): the `stripe.subscription` block configures a monthly plan; requires Connect payouts enabled (400 SUBSCRIPTION_REQUIRES_CONNECT otherwise — a hard error, unlike the soft warnings[]).
```ts
setMonetization(credentials, siteId, monetization, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The app owner's credentials (owner-scoped JWT). |
| `siteId` | `string` | required | The site/app id to operate on. |
| `monetization` | `Monetization` | required | The paywall config to save (enabled, payment_mode, stripe/agent, revenue_share_bps). |
| `config` | `FlowstackClientConfig` | since 0.2.6 | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | The saved config. `warnings` (0.2.9+) lists non-blocking config issues — e.g. a payment mode enabled without its rail fully set up. Surface them to the builder. |
### Examples
**Enable an AGENT-token paywall**
```ts
await setMonetization(credentials, siteId, {
enabled: true,
payment_mode: 'agent',
}, { appScope });
```
> ⚠️ **Warning:** Owner/management operation — call it with the **app owner's** credentials from a builder/dashboard/server context, not from inside the generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [getMonetization](https://flowstack.fun/docs/getMonetization) · [Monetization](https://flowstack.fun/docs/Monetization) · [getBuilderEarnings](https://flowstack.fun/docs/getBuilderEarnings) · [getConnectStatus](https://flowstack.fun/docs/getConnectStatus) · [MonetizationSubscriptionConfig](https://flowstack.fun/docs/MonetizationSubscriptionConfig)
*Source: src/api/client.ts (App Monetization, P0-155)*
## startConnectOnboarding
*utility · since 0.2.9 · ⚠ not for built apps* — Client function: create (or reuse) the builder's Stripe Connect Express account and get an onboarding link.
```ts
import { startConnectOnboarding } from 'flowstack-sdk'
```
Non-React client function. Creates the Connect Express account if needed and returns a one-time onboarding URL — redirect the builder there. Stripe returns them to `return_url` on completion or `refresh_url` if the link expires. `POST /billing/connect/onboard`.
```ts
startConnectOnboarding(credentials, urls, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The builder's (app owner's) credentials. |
| `urls` | `{ return_url: string; refresh_url: string }` | required | Where Stripe sends the builder after onboarding completes (`return_url`) or when the link expires (`refresh_url`). |
| `config` | `FlowstackClientConfig` | since 0.2.9 | Client config (baseUrl, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | The onboarding link to redirect to, plus the account id. |
### Examples
**Kick off onboarding**
```ts
const res = await startConnectOnboarding(credentials, {
return_url: `${location.origin}/earnings?onboarded=1`,
refresh_url: `${location.origin}/earnings?retry=1`,
});
if (res.ok) window.location.href = res.data.url;
```
> ℹ️ **Note:** Onboarding links are single-use and short-lived. After the builder returns, call `getConnectStatus` to confirm `payouts_enabled` before enabling payout UI.
> ⚠️ **Warning:** Builder/owner operation — call with the **builder's** credentials from a dashboard/console context, not from inside a generated app's end-user runtime. `builtAppSafe` is `false`.
**See also:** [getConnectStatus](https://flowstack.fun/docs/getConnectStatus) · [requestBuilderPayout](https://flowstack.fun/docs/requestBuilderPayout)
*Source: src/api/client.ts (Builder Earnings, P0-152)*
## subscribeToApp
*utility · since 0.3.0* — Client function: open Stripe Checkout for an app's monthly subscription plan (P0-162).
```ts
import { subscribeToApp } from 'flowstack-sdk'
```
Non-React client function. Creates a Stripe Checkout session (`mode=subscription`) for the builder's monthly plan and returns its URL — redirect the end-user there. `POST /billing/app-access/subscribe`. React apps should prefer `useAppAccess().subscribe()`, which does the redirect for you.
```ts
subscribeToApp(credentials, siteId, urls, config?): Promise>
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `credentials` | `FlowstackCredentials` | required | The end-user's credentials (the subscriber, not the builder). |
| `siteId` | `string` | required | The app to subscribe to (typically config.appScope). |
| `urls` | `{ success_url: string; cancel_url: string }` | required | Where Stripe Checkout redirects after success/cancel (HTTPS or localhost). |
| `config` | `FlowstackClientConfig` | — | Client config (baseUrl, appScope, tenantId as needed). |
### Returns
| Field | Type | Description |
|---|---|---|
| — | `Promise>` | The Checkout URL to redirect the user to. |
### Examples
**Subscribe from a non-React context**
```ts
const res = await subscribeToApp(credentials, siteId, {
success_url: window.location.href,
cancel_url: window.location.href,
}, { appScope: siteId });
if (res.data?.url) window.location.href = res.data.url;
```
> ℹ️ **Note:** 409 `already_subscribed` if an active (or past_due) subscription exists; 409 `ALREADY_UNLOCKED` if the user owns lifetime access — a subscription would be strictly worse than what they have.
**See also:** [cancelAppSubscription](https://flowstack.fun/docs/cancelAppSubscription) · [useAppAccess](https://flowstack.fun/docs/useAppAccess) · [AppSubscriptionStatus](https://flowstack.fun/docs/AppSubscriptionStatus) · [MonetizationSubscriptionConfig](https://flowstack.fun/docs/MonetizationSubscriptionConfig)
*Source: src/api/client.ts (App Subscriptions, P0-162)*
## AppSubscriptionStatus
*type · since 0.3.0* — The end-user's subscription state for one app: status and billing period — status-only since 0.3.3/P0-167 (P0-162).
```ts
import type { AppSubscriptionStatus } from 'flowstack-sdk/paywall'
```
The `subscription` field of `useAppAccess()`. Since 0.3.3 (P0-167) a subscription is pure ACCESS: an open paid period means full access with no per-plan query allowance (compute is funded by the app owner at 1 credit/query). `queriesUsed`/`queriesPerMonth` were REMOVED in 0.3.3 — code reading them gets `undefined`.
```ts
interface AppSubscriptionStatus
```
### Returns
| Field | Type | Description |
|---|---|---|
| `state` | `'none' | 'active' | 'past_due' | 'canceled'` | 'none' = never subscribed. 'past_due' rides Stripe's retry window (access continues). 'canceled' works until currentPeriodEnd. |
| `currentPeriodEnd` | `string | null` | ISO timestamp the current billing period ends. |
| `cancelAtPeriodEnd` | `boolean` | True after cancelSubscription() — the plan will not renew. |
| `label` | `string | null` | Builder's plan label, e.g. "$5/mo — full access". Defaults to a derived label. |
> ⚠️ **Warning:** BREAKING in 0.3.3 (P0-167): `queriesUsed` and `queriesPerMonth` no longer exist — subscription access is status-only. Do not render allowance meters from this type; an open period (`state` active/past_due/canceled with `currentPeriodEnd` in the future) means full access.
**See also:** [useAppAccess](https://flowstack.fun/docs/useAppAccess) · [AppPaywall](https://flowstack.fun/docs/AppPaywall) · [subscribeToApp](https://flowstack.fun/docs/subscribeToApp) · [cancelAppSubscription](https://flowstack.fun/docs/cancelAppSubscription) · [MonetizationSubscriptionConfig](https://flowstack.fun/docs/MonetizationSubscriptionConfig)
*Source: src/wallet/types.ts (P0-162)*
## BuilderEarnings
*type · since 0.2.9 · ⚠ not for built apps* — Type: the builder's full earnings picture — both rails, self-query summary, per-app breakdown.
```ts
import type { BuilderEarnings } from 'flowstack-sdk'
```
Returned by `getBuilderEarnings`. Carries legacy aggregate fields (`earned_cents`, `paid_cents`, `available_cents`, `min_payout_cents`, `currency`) for pre-P0-152 consumers, plus `rails.stripe` (`StripeRailEarnings`, cents), `rails.agent` (`AgentRailEarnings`, wei/AGENT strings), `self_query` (own-app usage, excluded from earnings), and `apps: AppEarnings[]` (per-app rails + `action` such as `set_payout_wallet` with escrowed `release_wei`/`release_agent`), and since 0.3.3 (P0-167) optional `compute` (`ComputeSpendSummary`: credits_spent/queries/usd_cents) — the builder-funded compute spend (cost of goods, NOT earnings) at top level and per app.
```ts
interface BuilderEarnings { earned_cents: number; paid_cents: number; available_cents: number; min_payout_cents: number; currency: string; builder_user_id: string; as_of: string; rails: { stripe: StripeRailEarnings; agent: AgentRailEarnings }; self_query: SelfQuerySummary; compute?: ComputeSpendSummary; apps: AppEarnings[] }
```
### Returns
| Field | Type | Description |
|---|---|---|
| `rails.stripe` | `StripeRailEarnings` | Fiat rail in integer cents (`accrued_cents`, `pending_cents`, `available_cents`, `shortfall_cents`, `connect` flags). |
| `rails.agent` | `AgentRailEarnings` | On-chain rail — wei STRINGS plus human `*_agent` decimal strings; `action_needed` may be `set_payout_wallet`. |
| `apps` | `AppEarnings[]` | Per-app rails + self-query summary + pending action, keyed by `site_id`. |
> 🚫 **Critical:** All `*_wei` fields are strings — they overflow JS numbers. Use the paired `*_agent` human-decimal strings for display and BigInt for arithmetic.
> ℹ️ **Note:** 0.3.3 (P0-167): `compute` is spend, not revenue — do not add it to earnings totals. It reports what the owner's credits funded (1 credit/query).
**See also:** [getBuilderEarnings](https://flowstack.fun/docs/getBuilderEarnings) · [requestBuilderPayout](https://flowstack.fun/docs/requestBuilderPayout) · [Monetization](https://flowstack.fun/docs/Monetization)
*Source: src/types/index.ts (Builder Earnings, P0-152)*
## ConnectStatus
*type · since 0.2.9 · ⚠ not for built apps* — Type: the builder's Stripe Connect account state.
```ts
import type { ConnectStatus } from 'flowstack-sdk'
```
Returned by `getConnectStatus`. `payouts_enabled` is the flag that gates `requestBuilderPayout`; `details_submitted` distinguishes "started onboarding but unfinished" from "not connected".
```ts
interface ConnectStatus { connected: boolean; stripe_account_id?: string; payouts_enabled: boolean; details_submitted?: boolean }
```
### Returns
| Field | Type | Description |
|---|---|---|
| `connected` | `boolean` | A Connect account exists for this builder. |
| `payouts_enabled` | `boolean` | Stripe will accept transfers — the gate for payout UI. |
**See also:** [getConnectStatus](https://flowstack.fun/docs/getConnectStatus) · [startConnectOnboarding](https://flowstack.fun/docs/startConnectOnboarding)
*Source: src/types/index.ts (Builder Earnings, P0-152)*
## Monetization
*type · since 0.2.6 · ⚠ not for built apps* — Type: a built app's monetization (paywall) config — Stripe and/or AGENT-token payment.
```ts
import type { Monetization } from 'flowstack-sdk'
```
The paywall/pricing shape read by `getMonetization` and written by `setMonetization`. `enabled` false/absent means no paywall. 0.3.0 (P0-162): `stripe.subscription` (MonetizationSubscriptionConfig) adds a monthly plan — $X/month for N queries/month on the builder's Stripe Connect rails.
```ts
interface Monetization { enabled?: boolean; payment_mode?: 'stripe' | 'agent' | 'both'; stripe?: MonetizationStripeConfig; agent?: MonetizationAgentConfig; revenue_share_bps?: number }
```
### Returns
| Field | Type | Description |
|---|---|---|
| `enabled` | `boolean?` | Master switch — false/absent = no paywall. |
| `payment_mode` | `'stripe' | 'agent' | 'both'?` | Which payment rail(s) the paywall offers. |
| `revenue_share_bps` | `number?` | Platform fee in basis points (default 2000 = 20%). |
| `stripe.subscription` | `MonetizationSubscriptionConfig?` | Monthly subscription plan (price_cents ≥ 200, label) — pure access since 0.3.3/P0-167, no query allowance. Requires Stripe Connect payouts enabled — the backend rejects with 400 SUBSCRIPTION_REQUIRES_CONNECT otherwise. stripe_price_id is server-managed. |
**See also:** [getMonetization](https://flowstack.fun/docs/getMonetization) · [setMonetization](https://flowstack.fun/docs/setMonetization) · [MonetizationSubscriptionConfig](https://flowstack.fun/docs/MonetizationSubscriptionConfig)
*Source: src/types/index.ts (App Monetization, P0-155)*
## MonetizationSubscriptionConfig
*type · since 0.3.0 · ⚠ not for built apps* — Monthly subscription plan config: $X/month for full access on the builder's Stripe Connect rails — pure access revenue since 0.3.3/P0-167 (P0-162).
```ts
import type { MonetizationSubscriptionConfig } from 'flowstack-sdk'
```
The `stripe.subscription` block inside `Monetization`. Since 0.3.3 (P0-167) a subscription is pure ACCESS revenue — no per-plan query allowance; compute is funded by the app owner at the canonical credit rate (1 credit/query). `queries_per_month` and `overage_mode` were REMOVED — the backend accepts-and-strips them from older clients. Enabling still requires the builder's Stripe Connect account to have payouts enabled (400 `SUBSCRIPTION_REQUIRES_CONNECT` otherwise). Payments are destination charges — the builder's share lands on their Connect account each invoice.
```ts
interface MonetizationSubscriptionConfig
```
### Returns
| Field | Type | Description |
|---|---|---|
| `price_cents` | `number` | Monthly price in USD cents. Minimum 200 ($2/mo) — Stripe fees make cheaper plans fee-dominated. |
| `label` | `string?` | Shown in the paywall UI, e.g. "$5/mo — full access". Defaults to a derived label. |
| `stripe_price_id` | `string?` | Stripe recurring Price id — server-managed (created on first enable, superseded on price change); client-supplied values are ignored. |
> ⚠️ **Warning:** BREAKING in 0.3.3 (P0-167): `queries_per_month` and `overage_mode` no longer exist. Sending them is harmless (backend strips), but plans no longer meter subscriber queries — budget owner-side compute instead (see BuilderEarnings.compute).
> ℹ️ **Note:** Price changes never affect existing subscribers — they stay on the Price (and allowance snapshot) they checked out with; the new Price applies to new subscribers only.
**See also:** [Monetization](https://flowstack.fun/docs/Monetization) · [setMonetization](https://flowstack.fun/docs/setMonetization) · [AppSubscriptionStatus](https://flowstack.fun/docs/AppSubscriptionStatus) · [subscribeToApp](https://flowstack.fun/docs/subscribeToApp) · [getConnectStatus](https://flowstack.fun/docs/getConnectStatus)
*Source: src/types/index.ts (App Monetization, P0-162)*
---
# Integrations & Automations
Service connections, integrations, and automations.
## call-http-integration
*recipe · since 0.2.8* — Use a registered HTTP API integration (notion, slack, linear, or owner-registered) from a built app via the agent
**Plane:** http_integration · **Requires:** capability `external_integration`
**Steps:**
1. `useAgent` — With capabilities ['external_integration','data_access'], prompt the agent to call the integration's endpoints and UPSERT results into the integration's declared collection shape.
2. `useCollection` — Read the synced collection reactively ({ layer: 'shared' }, refreshOnAgentComplete: true); the API is called on sync, never per render.
### Examples
**End-to-end example**
```tsx
import { useAgent, useCollection } from '@flowstack/sdk';
export function IssueBoard() {
const { query, isStreaming } = useAgent(undefined, {
capabilities: ['external_integration', 'data_access'],
});
const { documents: issues } = useCollection('linear_issues', {
layer: 'shared',
sort: { updated_at: -1 },
refreshOnAgentComplete: true,
});
return (
<>
{issues.map(i =>
{i.identifier}: {i.title}
)}
>
);
}
```
> ⚠️ **Warning:** Built-in HTTP integrations use the OWNER's credentials — do not build per-user connect flows for them.
> ⚠️ **Warning:** Cache API results in a collection and read via useCollection; never call the API per component render.
> ⚠️ **Warning:** Upsert on the integration's natural id (issue_id, page_id, message_ts) — never blind-insert.
**See also:** [call-stripe-api](https://flowstack.fun/docs/call-stripe-api) · [register-custom-api](https://flowstack.fun/docs/register-custom-api) · [connect-oauth-service](https://flowstack.fun/docs/connect-oauth-service) · [useAgent](https://flowstack.fun/docs/useAgent) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## call-stripe-api
*recipe · since 0.2.8* — Operate on the OWNER's Stripe account (revenue dashboards, customer lookups, invoice ops) from a built app
**Plane:** http_integration · **Requires:** capability `external_integration`
**Steps:**
1. `useAgent` — With capabilities ['external_integration','data_access'], prompt the agent to call the built-in stripe integration's endpoints (list_charges, list_customers, …) and UPSERT results into payment_events in the declared doc_schema shape.
2. `useCollection` — Read payment_events reactively ({ layer: 'shared' }, refreshOnAgentComplete: true) for the dashboard UI.
Writes `payment_events` (layer shared, writer agent) — required: event_id, kind, amount_cents, currency, created_at.
### Examples
**End-to-end example**
```tsx
import { useAgent, useCollection } from '@flowstack/sdk';
export function RevenuePanel() {
const { query, isStreaming } = useAgent(undefined, {
capabilities: ['external_integration', 'data_access'],
});
const { documents: events } = useCollection('payment_events', {
layer: 'shared',
sort: { created_at: -1 },
limit: 100,
refreshOnAgentComplete: true,
});
const totalCents = events.reduce((s, e) => s + (e.amount_cents ?? 0), 0);
return (
<>
${(totalCents / 100).toFixed(2)}
>
);
}
```
> ⚠️ **Warning:** For CHARGING USERS OF THE APP use the platform paywall (paywall-app recipe / setMonetization) — NOT raw Stripe calls.
> ⚠️ **Warning:** This recipe reads/operates the OWNER'S Stripe account via the owner-registered integration.
> ⚠️ **Warning:** Never persist card numbers, emails, or full customer objects; store opaque ids + amounts only.
**See also:** [paywall-app](https://flowstack.fun/docs/paywall-app) · [call-http-integration](https://flowstack.fun/docs/call-http-integration) · [register-custom-api](https://flowstack.fun/docs/register-custom-api) · [useAgent](https://flowstack.fun/docs/useAgent) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## connect-google-services
*recipe · since 0.2.8* — Let each user connect Google and use the sub-services their scopes actually grant (analytics, ads, drive, youtube)
**Plane:** oauth_connection · **Requires:** capability `external_integration`, end-user connection `google`
**Steps:**
1. `useConnections` — Call connect('google', ['drive', 'analytics']) with ONLY the sub-services the app needs; scopes gate what the agent can do.
2. `useConnections` — After connect, read the connection's services list — render features per granted sub-service, not per connected=true.
3. `useAgent` — With capabilities ['external_integration','data_access'], prompt the agent to sync the sub-service's data (e.g. Drive file metadata) into its collection in the declared doc_schema shape.
4. `useCollection` — Read the synced collection reactively ({ layer: 'user' }, refreshOnAgentComplete: true).
### Examples
**End-to-end example**
```tsx
import { useConnections, useAgent, useCollection } from '@flowstack/sdk';
export function DrivePanel() {
const { connections, connect } = useConnections();
const google = connections.find(c => c.provider === 'google');
const hasDrive = google?.connected && google.services?.includes('drive');
const { query } = useAgent(undefined, {
capabilities: ['external_integration', 'data_access'],
});
const { documents: files } = useCollection('drive_files', {
layer: 'user',
sort: { modified_at: -1 },
refreshOnAgentComplete: true,
});
if (!hasDrive) {
return ;
}
return (
<>
{files.map(f =>
{f.name}
)}
>
);
}
```
> ⚠️ **Warning:** Google grants are SCOPE-gated per sub-service — check connection.services, not just connected=true.
> ⚠️ **Warning:** Request only the sub-services the app needs at connect time; over-asking hurts consent rates.
> ⚠️ **Warning:** Per-END-USER connection: each user connects their own account; store synced data in layer 'user'.
**See also:** [connect-strava](https://flowstack.fun/docs/connect-strava) · [connect-oauth-service](https://flowstack.fun/docs/connect-oauth-service) · [useConnections](https://flowstack.fun/docs/useConnections) · [useAgent](https://flowstack.fun/docs/useAgent) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## connect-oauth-service
*recipe · since 0.2.8* — Let each user connect an OAuth service (github, reddit, twitter, strava, google) and work with their data in the app
**Plane:** oauth_connection · **Requires:** capability `external_integration`
**Steps:**
1. `useConnections` — Render connect UI gated on the provider's connection state; call connect(''); handle error_code on the result (consent_denied, state_mismatch, exchange_failed).
2. `useAgent` — With capabilities ['external_integration','data_access'], prompt the agent to fetch the user's data and UPSERT it into the provider's collection in the shape its connection contract declares.
3. `useCollection` — Read the provider's collection reactively ({ layer: 'user' }, refreshOnAgentComplete: true) — never parse chat text for data.
### Examples
**End-to-end example**
```tsx
import { useConnections, useAgent, useCollection } from '@flowstack/sdk';
export function GithubPanel() {
const { connections, connect } = useConnections();
const github = connections.find(c => c.provider === 'github');
const { query } = useAgent(undefined, {
capabilities: ['external_integration', 'data_access'],
});
const { documents: repos } = useCollection('github_repos', {
layer: 'user',
sort: { updated_at: -1 },
refreshOnAgentComplete: true,
});
if (!github?.connected) {
return ;
}
return (
<>
{repos.map(r =>
{r.full_name} ★{r.stars}
)}
>
);
}
```
> ⚠️ **Warning:** OAuth connections are per-END-USER (popup flow) — never build an owner-key input form for these providers.
> ⚠️ **Warning:** Gate every feature on live useConnections state; connection can be revoked between sessions.
> ⚠️ **Warning:** Sync into layer 'user' collections and upsert on the provider's natural id — never blind-insert.
**See also:** [connect-strava](https://flowstack.fun/docs/connect-strava) · [connect-google-services](https://flowstack.fun/docs/connect-google-services) · [useConnections](https://flowstack.fun/docs/useConnections) · [useAgent](https://flowstack.fun/docs/useAgent) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## connect-strava
*recipe · since 0.2.8* — Let each user connect their Strava account and work with their activities in the app
**Plane:** oauth_connection · **Requires:** capability `external_integration`, end-user connection `strava`
**Steps:**
1. `useConnections` — Render connect UI; call connect('strava'); handle error_code on the postMessage result (0.2.8+).
2. `useAgent` — With capabilities ['external_integration','data_access'], prompt the agent to fetch recent activities and UPSERT them into strava_activities (layer user) in exactly the doc_schema shape.
3. `useCollection` — Read strava_activities reactively ({ layer: 'user' }, refreshOnAgentComplete: true) — never parse chat text for data.
Writes `strava_activities` (layer user, writer agent) — required: activity_id, name, sport_type, start_date, distance_m, moving_time_s.
### Examples
**End-to-end example**
```tsx
import { useConnections, useAgent, useCollection } from '@flowstack/sdk';
export function StravaPanel() {
const { connections, connect, isLoading } = useConnections();
const strava = connections.find(c => c.provider === 'strava');
const { query, isStreaming } = useAgent(undefined, {
capabilities: ['external_integration', 'data_access'],
});
const { documents: activities } = useCollection('strava_activities', {
layer: 'user',
sort: { start_date: -1 },
limit: 50,
refreshOnAgentComplete: true,
});
if (isLoading) return
Loading…
;
if (!strava?.connected) {
return ;
}
return (
<>
{activities.map(a => (
{a.name} — {(a.distance_m / 1000).toFixed(1)} km
))}
>
);
}
```
> ⚠️ **Warning:** Strava is per-END-USER: gate sync UI on useConnections state, never assume connected.
> ⚠️ **Warning:** Write synced activities to layer 'user' — activity data is private by default.
> ⚠️ **Warning:** Dedupe on activity_id when re-syncing; the agent must upsert, not blind-insert.
**See also:** [reason-write-read](https://flowstack.fun/docs/reason-write-read) · [connect-oauth-service](https://flowstack.fun/docs/connect-oauth-service) · [useConnections](https://flowstack.fun/docs/useConnections) · [useAgent](https://flowstack.fun/docs/useAgent) · [useCollection](https://flowstack.fun/docs/useCollection)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## register-custom-api
*recipe · since 0.2.8* — Let the app OWNER register any HTTPS REST API as an integration the agent can call as a tool
**Plane:** http_integration · **Requires:** capability `external_integration`
**Steps:**
1. `useIntegrations` — Owner-side settings UI: create({ name, description, base_url, auth_type, auth_config, endpoints }) — creds are encrypted at rest; the frontend never sees raw secrets after creation.
2. `useAgent` — With capabilities ['external_integration','data_access'], prompt the agent to call the new integration's endpoints by name and write results into a collection.
3. `useCollection` — Read that collection reactively for the app UI (refreshOnAgentComplete: true).
### Examples
**End-to-end example**
```tsx
import { useIntegrations } from '@flowstack/sdk';
export function RegisterShopify() {
const { integrations, create, isLoading } = useIntegrations();
if (integrations.some(i => i.name === 'shopify')) return
Shopify registered.
;
return (
);
}
```
> ⚠️ **Warning:** This is the OWNER flow (registered once, creds encrypted) — for per-user accounts use the OAuth plane (useConnections) instead.
> ⚠️ **Warning:** base_url must be HTTPS and public; private/internal IPs are rejected (SSRF guard).
> ⚠️ **Warning:** Collect the API key via a form field at registration time — never hardcode secrets in app source.
**See also:** [call-http-integration](https://flowstack.fun/docs/call-http-integration) · [call-stripe-api](https://flowstack.fun/docs/call-stripe-api) · [connect-oauth-service](https://flowstack.fun/docs/connect-oauth-service) · [useIntegrations](https://flowstack.fun/docs/useIntegrations) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: sync-recipes.mjs ← https://sage-api.flowstack.fun/recipes.json*
## useAutomations
*hook · ⚠ not for built apps* — Schedule agent prompts on a cron schedule (via AWS EventBridge) with silent, email, webhook, or file delivery.
```ts
import { useAutomations } from 'flowstack-sdk'
```
Schedule agent prompts on a cron schedule via AWS EventBridge. Results can be delivered silently (stored only), by email, webhook, or as a downloadable file.
Schedule format: **5-field Unix cron** `"minute hour dom month dow"`.
**Output config options:**
| `type` | Delivers via | Extra fields |
|---|---|---|
| `"silent"` | Stored only (default) | — |
| `"email"` | Email | `to`, `subject_template`, `format` |
| `"webhook"` | HTTP POST | `url`, `headers`, `format` |
| `"file"` | Downloadable URL in `output_url` | `format` (`"csv"` \| `"json"` \| `"pdf"`) |
```ts
const { automations, isLoading, error, create, update, remove, pause, resume, runNow, getRuns, refresh } = useAutomations()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `automations` | `Automation[]` | Scheduled automations, e.g. `[{automation_id, name, schedule, status, last_run_at, run_count, ...}]`. |
| `isLoading` | `boolean` | Request in flight. |
| `error` | `string | null` | Last error message. |
| `create` | `(input) => Promise` | Create a scheduled automation (name, prompt, schedule, timezone, target_agents, output_config). |
| `update` | `(id, input) => Promise` | Update an automation. |
| `remove` | `(id) => Promise` | Delete an automation. |
| `pause` | `(id) => Promise` | Pause a schedule. |
| `resume` | `(id) => Promise` | Resume a paused schedule. |
| `runNow` | `(id) => Promise<{ invoked: boolean } | null>` | Run once immediately, ignoring the schedule. |
| `getRuns` | `(id, limit?) => Promise` | Fetch recent run results, e.g. `[{run_id, status, started_at, duration_ms, credits_used, output_summary, output_url}]`. |
| `refresh` | `() => Promise` | Re-fetch the automations list. |
### Examples
**Schedule a daily digest and manage runs**
```tsx
import { useAutomations } from 'flowstack-sdk';
const { automations, create, pause, resume, runNow, getRuns } = useAutomations();
// Daily sales digest — weekdays at 9 AM Eastern
await create({
name: 'Daily sales digest',
prompt: "Pull yesterday's orders from Shopify, summarize revenue by product and region, flag any anomalies.",
schedule: '0 9 * * 1-5',
timezone: 'America/New_York',
target_agents: ['shopify_analyst'], // routes to a specific agent persona; omit for default
output_config: {
type: 'email',
to: 'team@company.com',
subject_template: 'Sales digest — {date}',
},
});
// Run once right now (ignores schedule)
await runNow(automationId);
// Pause / resume
await pause(automationId);
await resume(automationId);
// Get last 10 run results
const runs = await getRuns(automationId, 10);
// [{run_id, status, started_at, duration_ms, credits_used, output_summary, output_url}]
```
> ℹ️ **Note:** Schedule format is **5-field Unix cron** `"minute hour dom month dow"`. Set `target_agents` to route to a specific agent persona; omit it for the default agent.
**See also:** [useIntegrations](https://flowstack.fun/docs/useIntegrations) · [useConnections](https://flowstack.fun/docs/useConnections) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#useautomations*
## useConnections
*hook* — Manage a user's external service connections (Google, Reddit, Strava, Twitter/X) so the AI agent can access their data.
```ts
import { useConnections } from 'flowstack-sdk'
```
Every built app should include a Settings page where users connect external services. Without connecting, the AI agent cannot access Google Analytics, Drive, Ads, YouTube, Reddit, Strava, or Twitter data.
Include this Settings page in every built app's navigation — the AI chat agent can only access services the user has connected.
```ts
const { connections, connect, disconnect, isLoading } = useConnections()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `connections` | `ConnectionStatus` | Per-provider status, e.g. `connections.google.connected` (boolean), `connections.google.analytics` (boolean — specific service), `connections.google.email` (string if connected), `connections.reddit.connected`, `connections.strava.connected`, `connections.twitter.connected`. |
| `connect` | `(provider: string, services?: string[]) => void` | Open the OAuth popup for a provider. For Google pass specific services (`['analytics', 'drive']`) or `['all']`; other providers take no services arg. |
| `disconnect` | `(provider: string) => void` | Disconnect a provider, e.g. `disconnect('google')`. |
| `isLoading` | `boolean` | Connection request in flight. |
### Examples
**Check status and connect/disconnect**
```tsx
import { useConnections } from 'flowstack-sdk';
const { connections, connect, disconnect, isLoading } = useConnections();
// Check status
connections.google.connected // true | false
connections.google.analytics // true | false (specific service)
connections.google.email // "user@gmail.com" if connected
connections.reddit.connected // true | false
connections.strava.connected // true | false
connections.twitter.connected // true | false
// Connect — opens OAuth popup
connect('google', ['analytics', 'drive']); // specific Google services
connect('google', ['all']); // all Google services
connect('reddit'); // Reddit
connect('strava'); // Strava
connect('twitter'); // Twitter/X
// Disconnect
disconnect('google');
disconnect('reddit');
```
**Settings page pattern**
```tsx
function SettingsPage() {
const { connections, connect, disconnect } = useConnections();
const services = [
{ key: 'google', label: 'Google', sublabel: 'Analytics, Ads, Drive, YouTube', services: ['all'] as const },
{ key: 'reddit', label: 'Reddit', sublabel: 'Feed access' },
{ key: 'strava', label: 'Strava', sublabel: 'Activity data' },
{ key: 'twitter', label: 'Twitter / X', sublabel: 'Timeline and bookmarks' },
];
return (
Connected Services
Connect your accounts so the AI assistant can access your data.
{services.map(({ key, label, sublabel, services: svc }) => {
const status = connections[key as keyof typeof connections];
return (
{label}
{sublabel}
{status.connected
?
: }
);
})}
);
}
```
> ℹ️ **Note:** Include a Settings page using this hook in every built app's navigation. The AI chat agent can only access services the user has connected.
**See also:** [useIntegrations](https://flowstack.fun/docs/useIntegrations) · [useDataSources](https://flowstack.fun/docs/useDataSources) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#service-connections-useconnections*
## useIntegrations
*hook · ⚠ not for built apps* — Register any HTTPS REST API as a named integration the agent can call as a tool, with credentials encrypted at rest.
```ts
import { useIntegrations } from 'flowstack-sdk'
```
Register any HTTPS REST API as a named integration that the agent can call as a tool. Credentials are encrypted at rest; raw secrets are never returned after creation.
Supported `auth_type` values: `'bearer'`, `'api_key_header'`, `'api_key_query'`, `'basic'`, `'none'`.
```ts
const { integrations, isLoading, error, create, update, remove, get, refresh } = useIntegrations()
```
### Returns
| Field | Type | Description |
|---|---|---|
| `integrations` | `Integration[]` | Registered integrations, e.g. `[{integration_id, name, base_url, auth_type, endpoint_count, ...}]`. |
| `isLoading` | `boolean` | Request in flight. |
| `error` | `string | null` | Last error message. |
| `create` | `(input) => Promise` | Register a new integration (name, description, base_url, auth_type, auth_config, endpoints). |
| `update` | `(id, input) => Promise` | Update an integration, e.g. rotate credentials. |
| `remove` | `(id) => Promise` | Delete an integration. |
| `get` | `(id) => Promise` | Fetch a single integration, including its full endpoint list. |
| `refresh` | `() => Promise` | Re-fetch the integrations list. |
### Examples
**Register, update, and remove an integration**
```tsx
import { useIntegrations } from 'flowstack-sdk';
const { integrations, create, update, remove, isLoading } = useIntegrations();
// Register Shopify
await create({
name: 'Shopify',
description: 'Shopify Admin API for order and product management',
base_url: 'https://my-store.myshopify.com/admin/api/2024-01',
auth_type: 'bearer', // 'bearer' | 'api_key_header' | 'api_key_query' | 'basic' | 'none'
auth_config: { token: 'shpat_xxx' },
endpoints: [
{ name: 'list_orders', method: 'GET', path: '/orders.json' },
{ name: 'get_order', method: 'GET', path: '/orders/{id}.json' },
{ name: 'cancel_order',method: 'POST', path: '/orders/{id}/cancel.json' },
],
});
// Update credentials
await update(id, { auth_config: { token: 'new_token' } });
// Remove
await remove(id);
```
> ℹ️ **Note:** Credentials are encrypted at rest; raw secrets are never returned after creation.
**See also:** [useAutomations](https://flowstack.fun/docs/useAutomations) · [useConnections](https://flowstack.fun/docs/useConnections) · [useDataSources](https://flowstack.fun/docs/useDataSources)
*Source: README.md#useintegrations*
---
# Utilities
API client, streaming helpers, and cache utilities.
## useFlowstackStatus
*hook* — Monitor the backend connection status, latency, and connectivity with optional polling.
```ts
import { useFlowstackStatus } from 'flowstack-sdk'
```
Backend connection monitoring. Optionally polls the backend on an interval and reports connection state and latency.
```ts
const { status, isConnected, latency, error, checkConnection } = useFlowstackStatus(options)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `options.pollInterval` | `number` | — | Polling interval in ms (e.g. `30000`). |
| `options.autoPoll` | `boolean` | — | Whether to poll automatically. |
### Returns
| Field | Type | Description |
|---|---|---|
| `status` | `'connected' | 'disconnected' | 'checking'` | Current connection status. |
| `isConnected` | `boolean` | Whether the backend is reachable. |
| `latency` | `number | null` | Round-trip latency in ms. |
| `error` | `string | null` | Last error message. |
| `checkConnection` | `() => Promise` | Manually trigger a connection check. |
### Examples
**Connection monitoring**
```tsx
const {
status, // 'connected' | 'disconnected' | 'checking'
isConnected, // boolean
latency, // number | null (ms)
error, // string | null
checkConnection, // () => Promise
} = useFlowstackStatus({
pollInterval: 30000,
autoPoll: true,
});
```
**See also:** [useAuth](https://flowstack.fun/docs/useAuth)
*Source: README.md#useflowstackstatus*
## API Client
*utility* — Lower-level promise-based API functions for direct backend access when hooks don't provide enough control.
```ts
import { listWorkspaces, executeQuery, uploadFile, flowstackFetch } from 'flowstack-sdk'
```
Direct API access — use these when you need lower-level control than the hooks provide.
All functions take `(credentials, ...params, config?)` and return `Promise>`. They cover auth (`login`, `register`), workspaces (`listWorkspaces`, `createWorkspace`, `getWorkspace`), datasets (`listDatasets`, `getDataset`, `getDatasetPreview`, `deleteDataset`), visualizations & reports (`listVisualizations`, `listReports`), models & scripts (`listModels`, `getModel`, `listScripts`), data sources (`listDataSources`, `createDataSource`, `testDataSource`, `deleteDataSource`), sites (`listSites`, `getSite`, `createSite`, `addSiteFile`, `publishStagedSite`, `deleteSite`), user management (`listUsers`, `getUser`, `updateUser`, `deleteUser`, `suspendUser`, `reactivateUser`, `getUserActivity`, `getUserStats`, `checkAdminPermissions`), conversations (`getConversationHistory`), query (`executeQuery`, `executeQueryWithConfig`), file upload (`uploadFile`), and the low-level `flowstackFetch` escape hatch.
```ts
fn(credentials, ...params, config?): Promise>
```
### Examples
**Available functions**
```tsx
import {
// Auth
login, register,
// Workspaces
listWorkspaces, createWorkspace, getWorkspace,
// Datasets
listDatasets, getDataset, getDatasetPreview, deleteDataset,
// Visualizations & Reports
listVisualizations, listReports,
// Models & Scripts
listModels, getModel, listScripts,
// Data Sources
listDataSources, createDataSource, testDataSource, deleteDataSource,
// Sites
listSites, getSite, createSite, addSiteFile, publishStagedSite, deleteSite,
// User Management
listUsers, getUser, updateUser, deleteUser, suspendUser, reactivateUser,
getUserActivity, getUserStats, checkAdminPermissions,
// Conversations
getConversationHistory,
// Query
executeQuery, executeQueryWithConfig,
// File Upload
uploadFile,
// Low-level
flowstackFetch,
} from 'flowstack-sdk';
```
> ℹ️ **Note:** Every function returns `Promise>` and accepts `credentials` as the first argument and an optional `config` as the last.
**See also:** [flowstackFetch](https://flowstack.fun/docs/flowstackFetch) · [useQuery](https://flowstack.fun/docs/useQuery) · [useWorkspace](https://flowstack.fun/docs/useWorkspace) · [useDatasets](https://flowstack.fun/docs/useDatasets)
*Source: README.md#api-client*
## CACHE_TTL
*utility* — Default time-to-live values for the SDK's per-resource client-side cache.
```ts
import { CACHE_TTL } from 'flowstack-sdk'
```
Constant exposing the default TTL values used by the cache utilities for per-resource client-side caching.
**See also:** [getCached](https://flowstack.fun/docs/getCached) · [setCached](https://flowstack.fun/docs/setCached) · [deleteCached](https://flowstack.fun/docs/deleteCached)
*Source: README.md#cache-utilities*
## deleteCached
*utility* — Remove a value from the generic per-resource client-side cache.
```ts
import { deleteCached } from 'flowstack-sdk'
```
Generic delete from the SDK's client-side cache. Part of the generic cache trio (`getCached` / `setCached` / `deleteCached`) for per-resource caching with TTL.
**See also:** [getCached](https://flowstack.fun/docs/getCached) · [setCached](https://flowstack.fun/docs/setCached) · [CACHE_TTL](https://flowstack.fun/docs/CACHE_TTL)
*Source: README.md#cache-utilities*
## flowstackFetch
*utility* — The low-level fetch escape hatch used by every API client function for authenticated requests to the Flowstack backend.
```ts
import { flowstackFetch } from 'flowstack-sdk'
```
`flowstackFetch` is the lowest-level building block exported from the API client. Use it when none of the higher-level functions (`listWorkspaces`, `executeQuery`, etc.) cover your call and you need to issue an authenticated request directly. Like the other client functions it accepts `credentials` and an optional `config` and returns `Promise>`.
**See also:** [apiClient](https://flowstack.fun/docs/apiClient) · [executeQuery](https://flowstack.fun/docs/executeQuery)
*Source: README.md#api-client*
## getCached
*utility* — Read a value from the generic per-resource client-side cache.
```ts
import { getCached, setCached, deleteCached, CACHE_TTL } from 'flowstack-sdk'
```
Generic read from the SDK's client-side cache. Part of the generic cache trio (`getCached` / `setCached` / `deleteCached`) that backs per-resource caching with TTL.
### Examples
**Cache utility imports**
```tsx
import {
// Generic
getCached, setCached, deleteCached,
CACHE_TTL,
// Resource-specific
getCachedWorkspaces, setCachedWorkspaces, invalidateWorkspacesCache,
getCachedDatasets, setCachedDatasets, invalidateDatasetsCache,
getCachedVisualizations, setCachedVisualizations, invalidateVisualizationsCache,
getCachedReports, setCachedReports, invalidateReportsCache,
getCachedSites, setCachedSites, invalidateSitesCache,
// Bulk invalidation
invalidateWorkspaceArtifacts,
invalidateAllUserCache,
} from 'flowstack-sdk';
```
**See also:** [setCached](https://flowstack.fun/docs/setCached) · [deleteCached](https://flowstack.fun/docs/deleteCached) · [CACHE_TTL](https://flowstack.fun/docs/CACHE_TTL)
*Source: README.md#cache-utilities*
## getCachedDatasets
*utility* — Read the cached list of datasets from the client-side cache.
```ts
import { getCachedDatasets } from 'flowstack-sdk'
```
Resource-specific cache reader for datasets. Pairs with `setCachedDatasets` and `invalidateDatasetsCache`.
**See also:** [setCachedDatasets](https://flowstack.fun/docs/setCachedDatasets) · [invalidateDatasetsCache](https://flowstack.fun/docs/invalidateDatasetsCache) · [useDatasets](https://flowstack.fun/docs/useDatasets)
*Source: README.md#cache-utilities*
## getCachedReports
*utility* — Read the cached list of reports from the client-side cache.
```ts
import { getCachedReports } from 'flowstack-sdk'
```
Resource-specific cache reader for reports. Pairs with `setCachedReports` and `invalidateReportsCache`.
**See also:** [setCachedReports](https://flowstack.fun/docs/setCachedReports) · [invalidateReportsCache](https://flowstack.fun/docs/invalidateReportsCache) · [useReports](https://flowstack.fun/docs/useReports)
*Source: README.md#cache-utilities*
## getCachedSites
*utility* — Read the cached list of sites from the client-side cache.
```ts
import { getCachedSites } from 'flowstack-sdk'
```
Resource-specific cache reader for sites. Pairs with `setCachedSites` and `invalidateSitesCache`.
**See also:** [setCachedSites](https://flowstack.fun/docs/setCachedSites) · [invalidateSitesCache](https://flowstack.fun/docs/invalidateSitesCache) · [useSites](https://flowstack.fun/docs/useSites)
*Source: README.md#cache-utilities*
## getCachedVisualizations
*utility* — Read the cached list of visualizations from the client-side cache.
```ts
import { getCachedVisualizations } from 'flowstack-sdk'
```
Resource-specific cache reader for visualizations. Pairs with `setCachedVisualizations` and `invalidateVisualizationsCache`.
**See also:** [setCachedVisualizations](https://flowstack.fun/docs/setCachedVisualizations) · [invalidateVisualizationsCache](https://flowstack.fun/docs/invalidateVisualizationsCache) · [useVisualizations](https://flowstack.fun/docs/useVisualizations)
*Source: README.md#cache-utilities*
## getCachedWorkspaces
*utility* — Read the cached list of workspaces from the client-side cache.
```ts
import { getCachedWorkspaces } from 'flowstack-sdk'
```
Resource-specific cache reader for workspaces. Pairs with `setCachedWorkspaces` and `invalidateWorkspacesCache`.
**See also:** [setCachedWorkspaces](https://flowstack.fun/docs/setCachedWorkspaces) · [invalidateWorkspacesCache](https://flowstack.fun/docs/invalidateWorkspacesCache) · [useWorkspace](https://flowstack.fun/docs/useWorkspace)
*Source: README.md#cache-utilities*
## invalidateAllUserCache
*utility* — Clear the entire client-side cache for the current user.
```ts
import { invalidateAllUserCache } from 'flowstack-sdk'
```
Bulk cache invalidation that clears everything cached for the current user — useful on logout or account switch.
**See also:** [invalidateWorkspaceArtifacts](https://flowstack.fun/docs/invalidateWorkspaceArtifacts)
*Source: README.md#cache-utilities*
## invalidateDatasetsCache
*utility* — Invalidate the cached list of datasets.
```ts
import { invalidateDatasetsCache } from 'flowstack-sdk'
```
Resource-specific cache invalidator for datasets. Pairs with `getCachedDatasets` and `setCachedDatasets`.
**See also:** [getCachedDatasets](https://flowstack.fun/docs/getCachedDatasets) · [setCachedDatasets](https://flowstack.fun/docs/setCachedDatasets) · [invalidateWorkspaceArtifacts](https://flowstack.fun/docs/invalidateWorkspaceArtifacts)
*Source: README.md#cache-utilities*
## invalidateReportsCache
*utility* — Invalidate the cached list of reports.
```ts
import { invalidateReportsCache } from 'flowstack-sdk'
```
Resource-specific cache invalidator for reports. Pairs with `getCachedReports` and `setCachedReports`.
**See also:** [getCachedReports](https://flowstack.fun/docs/getCachedReports) · [setCachedReports](https://flowstack.fun/docs/setCachedReports) · [invalidateWorkspaceArtifacts](https://flowstack.fun/docs/invalidateWorkspaceArtifacts)
*Source: README.md#cache-utilities*
## invalidateSitesCache
*utility* — Invalidate the cached list of sites.
```ts
import { invalidateSitesCache } from 'flowstack-sdk'
```
Resource-specific cache invalidator for sites. Pairs with `getCachedSites` and `setCachedSites`.
**See also:** [getCachedSites](https://flowstack.fun/docs/getCachedSites) · [setCachedSites](https://flowstack.fun/docs/setCachedSites) · [invalidateAllUserCache](https://flowstack.fun/docs/invalidateAllUserCache)
*Source: README.md#cache-utilities*
## invalidateVisualizationsCache
*utility* — Invalidate the cached list of visualizations.
```ts
import { invalidateVisualizationsCache } from 'flowstack-sdk'
```
Resource-specific cache invalidator for visualizations. Pairs with `getCachedVisualizations` and `setCachedVisualizations`.
**See also:** [getCachedVisualizations](https://flowstack.fun/docs/getCachedVisualizations) · [setCachedVisualizations](https://flowstack.fun/docs/setCachedVisualizations) · [invalidateWorkspaceArtifacts](https://flowstack.fun/docs/invalidateWorkspaceArtifacts)
*Source: README.md#cache-utilities*
## invalidateWorkspaceArtifacts
*utility* — Invalidate all cached artifacts (datasets, visualizations, reports, etc.) for a workspace.
```ts
import { invalidateWorkspaceArtifacts } from 'flowstack-sdk'
```
Bulk cache invalidation that clears all cached artifacts for a single workspace in one call.
**See also:** [invalidateAllUserCache](https://flowstack.fun/docs/invalidateAllUserCache) · [invalidateWorkspacesCache](https://flowstack.fun/docs/invalidateWorkspacesCache)
*Source: README.md#cache-utilities*
## invalidateWorkspacesCache
*utility* — Invalidate the cached list of workspaces.
```ts
import { invalidateWorkspacesCache } from 'flowstack-sdk'
```
Resource-specific cache invalidator for workspaces. Pairs with `getCachedWorkspaces` and `setCachedWorkspaces`.
**See also:** [getCachedWorkspaces](https://flowstack.fun/docs/getCachedWorkspaces) · [setCachedWorkspaces](https://flowstack.fun/docs/setCachedWorkspaces) · [invalidateWorkspaceArtifacts](https://flowstack.fun/docs/invalidateWorkspaceArtifacts)
*Source: README.md#cache-utilities*
## parseSSELine
*utility* — Parse a single Server-Sent Events line into a typed StreamEvent.
```ts
import { parseSSELine } from 'flowstack-sdk'
```
Parses one raw SSE line (e.g. `data: {"type":"content","content":"Hello"}`) from an agent stream into a typed `StreamEvent`. Use it when you control your own stream-reading loop; for full streams prefer `parseSSEStream` or `processSSEStream`.
```ts
const event = parseSSELine(line: string)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `line` | `string` | required | A single raw SSE line, e.g. `data: {...}`. |
### Returns
| Field | Type | Description |
|---|---|---|
| `event` | `StreamEvent` | The parsed stream event. |
### Examples
**Parse a single SSE line**
```tsx
const event = parseSSELine('data: {"type":"content","content":"Hello"}');
```
> ℹ️ **Note:** StreamEvent types: `content`, `delta`, `text`, `tool_start`, `tool_result`, `tool_error`, `progress`, `status`, `interrupt`, `done`, `error`.
**See also:** [parseSSEStream](https://flowstack.fun/docs/parseSSEStream) · [processSSEStream](https://flowstack.fun/docs/processSSEStream) · [useAgent](https://flowstack.fun/docs/useAgent)
*Source: README.md#streaming-utilities*
## parseSSEStream
*utility* — Async generator that yields typed StreamEvents from an agent SSE Response.
```ts
import { parseSSEStream } from 'flowstack-sdk'
```
Consumes a streaming `fetch` `Response` from an agent endpoint and yields typed `StreamEvent` objects as they arrive. Iterate it with `for await` and `switch` on `event.type` to handle content deltas, tool lifecycle events, and completion.
```ts
for await (const event of parseSSEStream(response)) { ... }
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `response` | `Response` | required | A streaming fetch Response from an agent stream. |
### Returns
| Field | Type | Description |
|---|---|---|
| `events` | `AsyncGenerator` | Async iterable of parsed stream events. |
### Examples
**Iterate a full SSE stream**
```tsx
for await (const event of parseSSEStream(response)) {
switch (event.type) {
case 'content': console.log(event.content); break;
case 'tool_start': console.log('Tool:', event.tool); break;
case 'tool_result': console.log('Result:', event.result); break;
case 'done': console.log('Complete'); break;
}
}
```
> ℹ️ **Note:** StreamEvent types: `content`, `delta`, `text`, `tool_start`, `tool_result`, `tool_error`, `progress`, `status`, `interrupt`, `done`, `error`.
**See also:** [parseSSELine](https://flowstack.fun/docs/parseSSELine) · [processSSEStream](https://flowstack.fun/docs/processSSEStream) · [useAgent](https://flowstack.fun/docs/useAgent) · [post-stream](https://flowstack.fun/docs/post-stream)
*Source: README.md#streaming-utilities*
## processSSEStream
*utility* — High-level helper that drives an agent SSE Response through callbacks.
```ts
import { processSSEStream } from 'flowstack-sdk'
```
The highest-level streaming helper: pass a streaming `Response` and a set of callbacks and `processSSEStream` parses the stream and invokes the matching callback for each event. Use it when you prefer callbacks over manually iterating `parseSSEStream`.
```ts
await processSSEStream(response, callbacks)
```
### Parameters
| Param | Type | Notes | Description |
|---|---|---|---|
| `response` | `Response` | required | A streaming fetch Response from an agent stream. |
| `callbacks.onContent` | `(text: string) => void` | — | Called for each content chunk. |
| `callbacks.onToolStart` | `(tool) => void` | — | Called when the agent starts a tool. |
| `callbacks.onDone` | `() => void` | — | Called when the stream completes. |
### Returns
| Field | Type | Description |
|---|---|---|
| `result` | `Promise` | Resolves when the stream is fully processed. |
### Examples
**Process a stream with callbacks**
```tsx
await processSSEStream(response, {
onContent: (text) => appendMessage(text),
onToolStart: (tool) => showToolIndicator(tool),
onDone: () => finalize(),
});
```
**See also:** [parseSSEStream](https://flowstack.fun/docs/parseSSEStream) · [parseSSELine](https://flowstack.fun/docs/parseSSELine) · [useAgent](https://flowstack.fun/docs/useAgent) · [post-stream](https://flowstack.fun/docs/post-stream)
*Source: README.md#streaming-utilities*
## setCached
*utility* — Write a value into the generic per-resource client-side cache with TTL.
```ts
import { setCached } from 'flowstack-sdk'
```
Generic write into the SDK's client-side cache. Part of the generic cache trio (`getCached` / `setCached` / `deleteCached`) for per-resource caching with TTL (see `CACHE_TTL`).
**See also:** [getCached](https://flowstack.fun/docs/getCached) · [deleteCached](https://flowstack.fun/docs/deleteCached) · [CACHE_TTL](https://flowstack.fun/docs/CACHE_TTL)
*Source: README.md#cache-utilities*
## setCachedDatasets
*utility* — Write the list of datasets into the client-side cache.
```ts
import { setCachedDatasets } from 'flowstack-sdk'
```
Resource-specific cache writer for datasets. Pairs with `getCachedDatasets` and `invalidateDatasetsCache`.
**See also:** [getCachedDatasets](https://flowstack.fun/docs/getCachedDatasets) · [invalidateDatasetsCache](https://flowstack.fun/docs/invalidateDatasetsCache) · [useDatasets](https://flowstack.fun/docs/useDatasets)
*Source: README.md#cache-utilities*
## setCachedReports
*utility* — Write the list of reports into the client-side cache.
```ts
import { setCachedReports } from 'flowstack-sdk'
```
Resource-specific cache writer for reports. Pairs with `getCachedReports` and `invalidateReportsCache`.
**See also:** [getCachedReports](https://flowstack.fun/docs/getCachedReports) · [invalidateReportsCache](https://flowstack.fun/docs/invalidateReportsCache) · [useReports](https://flowstack.fun/docs/useReports)
*Source: README.md#cache-utilities*
## setCachedSites
*utility* — Write the list of sites into the client-side cache.
```ts
import { setCachedSites } from 'flowstack-sdk'
```
Resource-specific cache writer for sites. Pairs with `getCachedSites` and `invalidateSitesCache`.
**See also:** [getCachedSites](https://flowstack.fun/docs/getCachedSites) · [invalidateSitesCache](https://flowstack.fun/docs/invalidateSitesCache) · [useSites](https://flowstack.fun/docs/useSites)
*Source: README.md#cache-utilities*
## setCachedVisualizations
*utility* — Write the list of visualizations into the client-side cache.
```ts
import { setCachedVisualizations } from 'flowstack-sdk'
```
Resource-specific cache writer for visualizations. Pairs with `getCachedVisualizations` and `invalidateVisualizationsCache`.
**See also:** [getCachedVisualizations](https://flowstack.fun/docs/getCachedVisualizations) · [invalidateVisualizationsCache](https://flowstack.fun/docs/invalidateVisualizationsCache) · [useVisualizations](https://flowstack.fun/docs/useVisualizations)
*Source: README.md#cache-utilities*
## setCachedWorkspaces
*utility* — Write the list of workspaces into the client-side cache.
```ts
import { setCachedWorkspaces } from 'flowstack-sdk'
```
Resource-specific cache writer for workspaces. Pairs with `getCachedWorkspaces` and `invalidateWorkspacesCache`.
**See also:** [getCachedWorkspaces](https://flowstack.fun/docs/getCachedWorkspaces) · [invalidateWorkspacesCache](https://flowstack.fun/docs/invalidateWorkspacesCache) · [useWorkspace](https://flowstack.fun/docs/useWorkspace)
*Source: README.md#cache-utilities*
---
# REST & SSE API
The Sage Data Science Agent HTTP/SSE API — REST API v2.0.0 (versioned independently of the SDK v0.3.3), 288 paths / 337 operations. Authoritative OpenAPI 3.1 spec: https://flowstack.fun/openapi.json
## Library
- `GET /library/code` — List Library Code
- `DELETE /library/code/{name}` — Delete Library Code
- `GET /library/code/{name}` — Get Library Code
- `GET /library/conversations` — List Conversations
- `DELETE /library/conversations/{conversation_id}` — Delete Conversation
- `GET /library/conversations/{conversation_id}` — Get Conversation
- `DELETE /library/conversations/{conversation_id}/star` — Unstar Conversation
- `POST /library/conversations/{conversation_id}/star` — Star Conversation
- `GET /library/conversations/recent` — List Recent Conversations
- `GET /library/datasets` — List Library Datasets
- `DELETE /library/datasets/{name}` — Delete Library Dataset
- `GET /library/datasets/{name}` — Get Library Dataset
- `GET /library/documents` — List Library Documents
- `DELETE /library/documents/{name}` — Delete Library Document
- `GET /library/documents/{name}` — Get Library Document
- `GET /library/models` — List Library Models
- `DELETE /library/models/{name}` — Delete Library Model
- `GET /library/models/{name}` — Get Library Model
- `GET /library/reports` — List Library Reports
- `DELETE /library/reports/{name}` — Delete Library Report
- `GET /library/reports/{name}` — Get Library Report
- `GET /library/search` — Search Library
- `DELETE /library/trash` — Empty Trash
- `GET /library/trash` — List Trash
- `DELETE /library/trash/{item_type}/{name}` — Hard Delete Trashed Item
- `POST /library/trash/{item_type}/{name}/restore` — Restore Trashed Item
- `GET /library/visualizations` — List Library Visualizations
- `DELETE /library/visualizations/{name}` — Delete Library Visualization
- `GET /library/visualizations/{name}` — Get Library Visualization
## Sites
- `GET /api/v1/sites` — List Sites
- `POST /api/v1/sites` — Create Site
- `DELETE /api/v1/sites/{site_id}` — Delete Site
- `GET /api/v1/sites/{site_id}` — Get Site
- `PATCH /api/v1/sites/{site_id}` — Rename Site Endpoint
- `GET /api/v1/sites/{site_id}/agents` — List Site Agents
- `POST /api/v1/sites/{site_id}/agents` — Create Site Agent
- `PATCH /api/v1/sites/{site_id}/agents/{agent_name}` — Update Site Agent
- `DELETE /api/v1/sites/{site_id}/alias` — Remove Alias
- `POST /api/v1/sites/{site_id}/alias` — Set Alias
- `GET /api/v1/sites/{site_id}/data-plan` — Get Site Data Plan
- `PUT /api/v1/sites/{site_id}/data-plan` — Set Site Data Plan
- `PUT /api/v1/sites/{site_id}/files/{file_path}` — Stage Site File
- `POST /api/v1/sites/{site_id}/import-library` — Import Library Item
- `POST /api/v1/sites/{site_id}/promote` — Promote Version
- `POST /api/v1/sites/{site_id}/publish` — Publish Staged Site Endpoint
- `POST /api/v1/sites/{site_id}/publish-github` — Publish To Github
- `GET /api/v1/sites/{site_id}/source` — Get Site Source
- `DELETE /api/v1/sites/{site_id}/staged` — Clear Staged Files
- `GET /api/v1/sites/{site_id}/staged` — List Staged Files
- `POST /api/v1/sites/{site_id}/staged` — Stage Site Files
- `POST /api/v1/sites/{site_id}/upload-source` — Upload Source
- `GET /api/v1/sites/{site_id}/versions` — Get Site Versions
- `DELETE /api/v1/sites/{site_id}/versions/{version}` — Delete Version
- `POST /api/v1/sites/daily/generate` — Generate Daily Site
- `GET /api/v1/sites/daily/latest` — Get Latest Daily Site
- `POST /api/v1/sites/migrate-legacy` — Migrate Legacy Sites
## Billing
- `GET /billing/agent/balance` — Get Agent Balance
- `POST /billing/agent/sponsor-gas` — Sponsor Agent Gas
- `GET /billing/credits/purchased` — Get Purchased Credits
- `GET /billing/credits/status` — Get Credit Status
- `GET /billing/infer/balance` — Get Infer Balance
- `GET /billing/plans` — Get Plans
- `POST /billing/stripe/buy-agent` — Create Agent Checkout
- `POST /billing/stripe/checkout` — Create Checkout Session
- `POST /billing/stripe/mint-retry` — Retry Stripe Mint
- `GET /billing/stripe/mint/health` — Stripe Mint Health
- `GET /billing/stripe/mints` — List Stripe Mints
- `POST /billing/stripe/subscribe` — Create Subscription Checkout
- `POST /billing/stripe/webhook` — Stripe Webhook
- `POST /billing/subscription/cancel` — Cancel Subscription
- `GET /billing/subscription/status` — Get Subscription Status
- `GET /billing/usage` — Get Usage
- `GET /billing/usage/monthly` — Get Monthly Usage
- `GET /billing/usage/today` — Get Today Usage
- `GET /billing/usage/user` — Get User Usage
## Admin
- `GET /admin/keys` — Admin List Keys
- `POST /admin/keys/{key_id}/revoke` — Admin Revoke Key
- `GET /admin/mongo-capacity` — Mongo Capacity
- `POST /admin/query` — Process Query
- `GET /admin/requests` — Admin List Requests
- `POST /admin/requests/{request_id}/approve` — Admin Approve Request
- `POST /admin/requests/{request_id}/deny` — Admin Deny Request
- `POST /admin/reset-context` — Reset Conversation Context
- `GET /admin/roles` — Admin List Roles
- `GET /admin/session` — Get Session Info
- `DELETE /admin/sessions/{session_id}/clear` — Clear Session Conversation Endpoint
- `POST /admin/subscriptions/grant` — Grant Subscription
- `GET /admin/users` — Admin List Users
- `GET /admin/users/{user_id}` — Admin Get User
- `POST /admin/visualize` — Create Visualization
## Provider Credentials
- `GET /api/v1/user/provider-credentials` — List Provider Credentials
- `POST /api/v1/user/provider-credentials` — Create Provider Credential
- `DELETE /api/v1/user/provider-credentials/{credential_id}` — Delete Provider Credential
- `GET /api/v1/user/provider-credentials/{credential_id}` — Get Provider Credential
- `PUT /api/v1/user/provider-credentials/{credential_id}` — Update Provider Credential
- `POST /api/v1/user/provider-credentials/{credential_id}/set-default` — Set Default Provider Credential
- `GET /api/v1/user/provider-credentials/code-interpreter` — Get Code Interpreter Credential
- `POST /api/v1/user/provider-credentials/code-interpreter` — Create Code Interpreter Credential
- `GET /api/v1/user/provider-credentials/components` — Get All Component Credentials
- `GET /api/v1/user/provider-credentials/default` — Get Default Provider Credential
- `GET /api/v1/user/provider-credentials/purpose/{purpose}` — Get Credential For Purpose
- `POST /api/v1/user/provider-credentials/purpose/{purpose}` — Create Credential For Purpose
- `GET /api/v1/user/provider-credentials/purposes` — Get Supported Purposes
- `GET /api/v1/user/provider-credentials/routing-preference` — Get Routing Preference
- `PUT /api/v1/user/provider-credentials/routing-preference` — Set Routing Preference
## Workspaces
- `GET /api/v1/workspaces` — List Workspaces V1
- `DELETE /api/v1/workspaces/{workspace_id}/pii-allowlist` — Remove Pii Allowlist Term
- `GET /api/v1/workspaces/{workspace_id}/pii-allowlist` — Get Pii Allowlist
- `POST /api/v1/workspaces/{workspace_id}/pii-allowlist` — Add Pii Allowlist Term
- `GET /api/v1/workspaces/{workspace_id}/pii-settings` — Get Pii Settings
- `PATCH /api/v1/workspaces/{workspace_id}/pii-settings` — Update Pii Settings
- `GET /tenants/{tenant_id}/workspaces` — List Tenant Workspaces
- `POST /tenants/{tenant_id}/workspaces` — Create Workspace
- `DELETE /tenants/{tenant_id}/workspaces/{workspace_id}` — Delete Workspace
- `GET /tenants/{tenant_id}/workspaces/{workspace_id}` — Get Workspace Details
- `PATCH /tenants/{tenant_id}/workspaces/{workspace_id}` — Update Workspace
- `GET /tenants/{tenant_id}/workspaces/{workspace_id}/usage` — Get Workspace Usage
- `POST /workspaces/{workspace_id}/query` — Query Workspace
- `GET /workspaces/{workspace_id}/sessions` — List Workspace Sessions
## Authentication
- `POST /auth/auth/token/refresh` — Refresh Token
- `POST /auth/google/login` — Google Login
- `POST /auth/login` — Login User
- `POST /auth/register` — Register User
- `POST /auth/resend-verification` — Resend Verification
- `POST /auth/user/login` — User Login
- `GET /auth/user/me` — User Me
- `POST /auth/user/register` — User Register
- `GET /auth/user/status` — User Registration Status
- `POST /auth/user/verify` — User Verify
- `GET /auth/user/verify/{token}` — User Verify Link
- `POST /auth/verify` — Verify Email
## Automations
- `GET /automations` — List Automations
- `POST /automations` — Create Automation
- `GET /automations/_meta/limits` — Get Automation Limits
- `DELETE /automations/{automation_id}` — Delete Automation
- `GET /automations/{automation_id}` — Get Automation
- `PUT /automations/{automation_id}` — Update Automation
- `POST /automations/{automation_id}/pause` — Pause Automation
- `POST /automations/{automation_id}/resume` — Resume Automation
- `POST /automations/{automation_id}/run` — Run Automation Now
- `GET /automations/{automation_id}/runs` — List Automation Runs
- `GET /automations/{automation_id}/runs/{run_id}` — Get Automation Run
## Files
- `GET /api/v1/user/artifacts` — List User Artifacts
- `GET /api/v1/user/documents` — List User Documents
- `DELETE /data/cleanup/{session_id}` — Cleanup Session Data
- `GET /data/download/{session_id}/{file_type}/{filename}` — Download File
- `GET /data/files/{session_id}` — List Session Files
- `POST /data/sync/{session_id}` — Sync Session To Local
- `POST /data/upload/{session_id}` — Upload File To Session
- `POST /load-from-url` — Load Dataset From Url
- `POST /upload` — Upload File
- `POST /upload-document` — Upload Document
## Agents
- `GET /agents/registry` — List Agent Registry
- `POST /agents/route` — Route Agent
- `GET /agents/spend` — Agent Spend
- `GET /library/agents` — List Agents
- `DELETE /library/agents/{name}` — Delete Agent
- `GET /library/agents/{name}` — Get Agent
- `POST /library/agents/{name}/invoke` — Invoke Agent
- `GET /library/agents/{name}/runs` — List Agent Runs
- `POST /library/agents/upload` — Upload Agent
## Social OAuth
- `GET /auth/reddit/callback` — Reddit Callback
- `GET /auth/reddit/start` — Reddit Start
- `GET /auth/reddit/status` — Reddit Status
- `GET /auth/strava/callback` — Strava Callback
- `GET /auth/strava/start` — Strava Start
- `GET /auth/strava/status` — Strava Status
- `GET /auth/twitter/callback` — Twitter Callback
- `GET /auth/twitter/start` — Twitter Start
- `GET /auth/twitter/status` — Twitter Status
## Streaming
- `GET /agents` — List Agents
- `POST /prewarm` — Prewarm Swarm Endpoint
- `POST /stream` — Stream Query
- `POST /stream-simple` — Stream Query Simple
- `POST /stream/interrupt` — Interrupt Stream
- `POST /stream/pii-preview` — Pii Preview
- `POST /stream/resume` — Resume From Interrupt
- `GET /stream/status` — Agent Status
- `GET /swarm-cache-stats` — Get Swarm Cache Stats
## Conversations
- `GET /conversations` — Get Conversation History
- `DELETE /conversations/{session_id}` — Soft Delete Conversation
- `GET /conversations/{session_id}/messages` — Get Conversation Messages
- `POST /conversations/{session_id}/title` — Rename Conversation
- `POST /conversations/backfill-titles` — Backfill Auto Titles
- `GET /conversations/context` — Get Conversation Context
- `GET /conversations/scope` — List Conversations For Scope
- `POST /conversations/search` — Search Conversations
## Digest
- `GET /api/v1/digest/feed-catalog` — Get Feed Catalog
- `POST /api/v1/digest/generate` — Generate Digest Endpoint
- `GET /api/v1/digest/interests` — Get Interests
- `POST /api/v1/digest/interests` — Update Interests
- `GET /api/v1/digest/rss-feeds` — List Rss Feeds
- `POST /api/v1/digest/rss-feeds` — Add Rss Feed
- `DELETE /api/v1/digest/rss-feeds/{feed_id}` — Remove Rss Feed
- `GET /api/v1/digest/sources` — List Digest Sources
## MCP
- `GET /mcp` — Streamable Http Get
- `POST /mcp` — Streamable Http Endpoint
- `GET /mcp/` — Streamable Http Get
- `POST /mcp/` — Streamable Http Endpoint
- `POST /mcp/messages` — Messages Endpoint
- `GET /mcp/oauth/authorization-server` — Oauth Metadata
- `POST /mcp/oauth/token` — Oauth Token
- `GET /mcp/sse` — Sse Endpoint
## Admin - Providers
- `GET /admin/provider-credentials` — List Admin Provider Credentials
- `POST /admin/provider-credentials` — Create Admin Provider Credential
- `DELETE /admin/provider-credentials/{credential_id}` — Delete Admin Provider Credential
- `GET /admin/provider-credentials/am-i-admin` — Am I Tenant Admin
- `GET /admin/provider-credentials/existing` — List Existing Credentials
- `GET /admin/provider-credentials/live-model-map` — Get Live Model Map
- `POST /admin/provider-credentials/promote` — Promote Existing Credential
## Data Sources
- `GET /data-sources` — List Data Sources
- `POST /data-sources` — Create Data Source
- `DELETE /data-sources/{source_id}` — Delete Data Source
- `GET /data-sources/{source_id}` — Get Data Source
- `PUT /data-sources/{source_id}` — Update Data Source
- `POST /data-sources/{source_id}/test` — Test Data Source Connection
- `POST /data-sources/test-connection` — Test New Connection
## GitHub
- `POST /api/v1/github/import` — Import Repo
- `GET /api/v1/github/repos` — List Repos
- `POST /api/v1/github/webhook` — Github Webhook
- `GET /auth/github/callback` — Github Callback
- `POST /auth/github/disconnect` — Github Disconnect
- `GET /auth/github/start` — Github Start
- `GET /auth/github/status` — Github Status
## App Billing
- `POST /billing/app-access/checkout` — Create App Checkout
- `GET /billing/app-access/monetization/{site_id}` — Get Site Monetization
- `POST /billing/app-access/monetization/{site_id}` — Set Site Monetization Config
- `GET /billing/app-access/status` — Get App Access Status
- `POST /billing/app-access/subscribe` — Create App Subscription Checkout
- `POST /billing/app-access/subscription/cancel` — Cancel App Subscription
## Health Data
- `GET /api/v1/health-data/analysis/{analysis_id}` — Get Analysis Result
- `GET /api/v1/health-data/follow-up` — Follow Up Query
- `POST /api/v1/health-data/ingest` — Ingest Health Data
- `POST /api/v1/health-data/register-phone` — Register Phone
- `GET /api/v1/health-data/report-preview` — Preview Health Report
- `POST /api/v1/health-data/test-mms` — Test Mms Delivery
## Registrar
- `POST /api/v1/registrar/{domain}/toggle-renewal` — Toggle Renewal
- `POST /api/v1/registrar/{domain}/transfer-out` — Transfer Out
- `GET /api/v1/registrar/check` — Check Availability
- `GET /api/v1/registrar/my-domains` — List My Domains
- `GET /api/v1/registrar/prices` — Get Prices
- `POST /api/v1/registrar/purchase` — Purchase Domain
## User Data
- `GET /api/v1/user/collections` — List User Collections
- `DELETE /api/v1/user/collections/{collection}` — Delete User Collection
- `GET /api/v1/user/collections/{collection}/documents` — Get Collection Documents
- `POST /api/v1/user/collections/{collection}/export` — Export User Collection
- `GET /api/v1/user/collections/{collection}/schema` — Get Collection Schema
- `GET /api/v1/user/data-overview` — Get Data Overview
## Wallet Auth
- `POST /auth/guest` — Issue Guest Token
- `POST /auth/privy-refresh` — Privy Refresh
- `POST /auth/privy-verify` — Privy Verify
- `POST /auth/wallet/link` — Link Wallet
- `GET /auth/wallet/nonce` — Get Siwe Nonce
- `POST /auth/wallet/verify` — Siwe Verify
## App Messaging
- `GET /apps/{app_scope}/messages` — Get Messages
- `POST /apps/{app_scope}/messages` — Send Message
- `POST /apps/{app_scope}/messages/{message_id}/read` — Mark Message Read
- `GET /apps/{app_scope}/threads` — Get Threads
- `POST /apps/{app_scope}/threads/{pair_key}/consent` — Open Thread
## Custom Domains
- `GET /api/v1/apps/{site_id}/domains` — List Domains
- `POST /api/v1/apps/{site_id}/domains` — Connect Domain
- `DELETE /api/v1/apps/{site_id}/domains/{domain}` — Disconnect Domain
- `POST /api/v1/apps/{site_id}/domains/{domain}/activate` — Activate Domain
- `GET /api/v1/apps/{site_id}/domains/{domain}/status` — Domain Status
## Google OAuth
- `GET /auth/google/callback` — Oauth Callback
- `POST /auth/google/revoke` — Revoke Oauth
- `GET /auth/google/start` — Start Oauth Flow
- `GET /auth/google/status` — Get Oauth Status
- `GET /auth/google/test` — Test Oauth Connection
## Integrations
- `GET /integrations` — List Integrations
- `POST /integrations` — Create Integration
- `DELETE /integrations/{integration_id}` — Delete Integration
- `GET /integrations/{integration_id}` — Get Integration
- `PUT /integrations/{integration_id}` — Update Integration
## Scripts
- `GET /scripts` — List Scripts
- `GET /scripts/{script_name}` — Get Script
- `GET /scripts/{script_name}/download` — Download Script
- `GET /scripts/detailed` — List Scripts Detailed
- `GET /tenants/{tenant_id}/scripts` — List Tenant Scripts
## Sessions
- `PUT /session/{session_id}/activity` — Heartbeat
- `GET /session/{session_id}/lock-status` — Get Lock Status
- `POST /session/claim` — Claim Session
- `GET /session/current` — Get Current Session
- `POST /session/release` — Release Session
## API Keys
- `GET /tenants/{tenant_id}/api-keys` — List Tenant Api Keys
- `POST /tenants/{tenant_id}/api-keys` — Create Tenant Api Key
- `DELETE /tenants/{tenant_id}/api-keys/{key_id}` — Revoke Tenant Api Key
- `GET /tenants/{tenant_id}/api-keys/stats` — Get Tenant Api Key Stats
## Builder Payouts
- `GET /billing/builder/earnings` — Builder Earnings
- `POST /billing/builder/payout` — Builder Payout
- `POST /billing/connect/onboard` — Connect Onboard
- `GET /billing/connect/status` — Connect Status
## Collections
- `POST /collections/delete` — Delete Documents
- `POST /collections/insert` — Insert Documents
- `GET /collections/query` — Query Collection
- `POST /collections/update` — Update Documents
## Datasets
- `GET /datasets` — List Datasets
- `GET /datasets/{dataset_name}/download` — Download Dataset
- `GET /session/{session_id}/visualizations` — Get Session Visualizations
- `GET /visualizations` — List Visualizations
## Identity
- `POST /auth/identity/link` — Link Identity
- `GET /auth/identity/links` — List My Links
- `DELETE /auth/identity/links/{auth_method_user_id}` — Unlink Identity
- `GET /auth/identity/orphaned-data` — Get Orphaned Data
## Observability
- `GET /observability/billing-metrics` — Get Billing Metrics
- `GET /observability/session/{session_id}` — Get Session Telemetry
- `GET /observability/tools-analytics` — Get Tools Analytics
- `GET /observability/workspace/{workspace_id}` — Get Workspace Telemetry
## Tools
- `GET /tools` — List Tools
- `POST /tools/execute` — Execute Tool
- `GET /tools/schemas` — Get Tool Schemas
- `GET /tools/summary` — Get Tools Summary
## Models
- `GET /models` — List Models
- `GET /models/{model_name}` — Get Model
- `GET /models/{model_name}/download` — Download Model
## Reports
- `GET /reports` — List Reports
- `GET /reports/{report_id}` — Get Report
- `GET /reports/{report_id}/download` — Download Report
## User Preferences
- `GET /user/model-preference` — Get Model Preference
- `PUT /user/model-preference` — Set Model Preference
- `GET /user/model-preference/options` — List Model Preference Options
## Visualizations
- `GET /tenants/{tenant_id}/datasets` — List Tenant Datasets
- `GET /tenants/{tenant_id}/visualizations` — List Tenant Visualizations
- `GET /tenants/{tenant_id}/workspaces/{workspace_id}/visualizations` — List Workspace Visualizations
## Health
- `GET /health` — Health Check
- `GET /health/detailed` — Detailed Health Check
## Mono-Sage
- `GET /mono/health` — Mono Health
- `POST /mono/stream` — Mono Stream
## Public Collections
- `GET /public/collections/{collection}/documents` — Public Query
- `POST /public/collections/{collection}/insert` — Public Insert
## Capabilities
- `GET /capabilities` — Get Capabilities
## Other
- `GET /` — Root
## Public Analytics
- `GET /analytics/public/agents` — Public Agents
## Recipes
- `GET /recipes.json` — Get Recipes
## Short URLs
- `GET /s/{short_id}` — Short Url Redirect
## Tool Invoke
- `POST /tool/invoke` — Invoke Tool
## Webhooks
- `POST /webhooks/twilio/inbound` — Twilio Inbound Sms