This is the full developer documentation for Foldspace
# Build AI-Native Product Experiences
> Foldspace is the AI-Native agentic interface for your product: an agent that interprets user intent and acts inside your app, with Analytics and Trust Lab built in.
### [User Guides](/user-guides/)
[For product & growth teams](/user-guides/)
[Get the most out of your agent. Follow best practices for building and tuning it, use Analytics to see how users engage and where it falls short, and validate every change in Trust Lab before it ships.](/user-guides/)
[Best practices Analytics Trust Lab Conversations Knowledge](/user-guides/)
### [Developer Guide](/start/introduction/)
[Build agentic experiences in code](/start/introduction/)
[Drop in the SDK and make your product act. Bind actions to your backend, render UI components inside the chat, sync live app state, and orchestrate task agents — all through typed, real APIs.](/start/introduction/)
[SDK Actions Chatterblocks Shared State Task Agents APIs](/start/introduction/)
Shared tools for both teams
[ Trust Lab ](/user-guides/trust-lab/)· [ Analytics](/user-guides/analytics/dashboard/)
# 404
> The page you were looking for could not be found.
## This page folded out of existence
[Section titled “This page folded out of existence”](#this-page-folded-out-of-existence)
The page you were looking for doesn’t exist or has moved.
[Back to home](/) · [Quickstart](/start/quickstart/) · [API Reference](/reference/overview/)
# Cookbook
> End-to-end, opinionated recipes that combine Foldspace primitives (actions, task agents, and UI) into a complete capability.
A **recipe** is complete and opinionated. Where a [guide](/guides/build-actions/) teaches one primitive (“how to author an action”, “how to call a task agent”), a recipe composes several primitives into a real, shippable capability, with the architecture, the routing logic, and the code all in one place.
Reach for a recipe when you know the *outcome* you want (“let users analyze a report and get recommendations”) but not yet the *shape*: which actions, which task agents, and how they wire together.
[Report insights (routing action) ](/cookbook/insights-action-routing/)One action asks the user which KPI to optimize, then routes the analysis to a specialized task agent and returns structured insights.
## How a recipe is structured
[Section titled “How a recipe is structured”](#how-a-recipe-is-structured)
Every recipe follows the same arc, so you can skim or execute:
* **What you’ll build**: the outcome, and the core idea in one paragraph.
* **Architecture**: a diagram of the pieces and how a request flows through them.
* **Steps**: build each piece in order (task agents, then the action, then the handler), with copy-ready configuration and code.
* **Test, evaluate, publish**: how to prove it works before it ships.
* **Best practices & extend it**: how to grow the pattern without a rewrite.
## Related
[Section titled “Related”](#related)
* [Build actions](/guides/build-actions/): define and implement the capabilities an agent runs.
* [Task agents](/user-guides/task-agents/): specialized sub-agents the main agent delegates to.
# Recipe: report insights with a routing action
> Build one action that asks the user which KPI to optimize, then routes the analysis to a specialized task agent and returns structured insights.
## What you’ll build
[Section titled “What you’ll build”](#what-youll-build)
An **Insights** capability: the user points the agent at a reporting or analytics report and gets back *decisions* — the drivers, the anomalies, and what to do next — not just a summary.
The trick is a single **routing action**. Instead of one giant “analyze everything” prompt, the action first has the agent ask a **qualifying question** — *what goal or KPI do you want to optimize?* — and then its handler **routes** that intent to the right specialized [task agent](/user-guides/task-agents/): a revenue analyst, a funnel analyst, a retention analyst, and so on. One action, many specialized analysts.
Why route instead of writing one mega-prompt:
* **Each analyst is focused.** A funnel expert and a churn expert need different reasoning; keeping them separate makes each sharper.
* **Each analyst is independently versioned, evaluated, cached, and tuned.** You can put a cheap model behind a simple summary and a stronger one behind cohort analysis.
* **The capability grows without a rewrite.** Adding a KPI means adding one task agent and one line in a routing map — the action and its handler don’t change.
Prerequisites
You should be comfortable [authoring an action](/user-guides/authoring-actions/), wiring its [`execute()` handler](/guides/executing-actions/), and calling a [task agent with `runTask()`](/reference/task-agent-api/). This recipe assembles those three into one capability.
## Architecture
[Section titled “Architecture”](#architecture)
```
sequenceDiagram
participant U as User
participant A as Main Agent
participant Act as Routing Action (execute)
participant T as Specialist Task Agent
U->>A: "Analyze last quarter's report"
A->>U: Which goal or KPI do you want to optimize?
U->>A: Retention
A->>Act: analyze_report_insights({ report, goal, kpi })
Act->>Act: Map kpi → taskKey
Act->>T: runTask(retention_churn_analyzer, { report, question })
T-->>Act: Structured insights (JSON)
Act-->>A: { kpi, summary, findings, recommendations }
A->>U: Reply grounded in the analyst's insights
```
The main agent handles the conversation and the qualifying question. The **routing action** is a thin dispatcher: it maps the chosen KPI to a task-agent key and calls it. The **task agent** does the heavy reasoning and returns a predictable JSON object the agent can relay.
## Step 1 — Build the specialist task agents
[Section titled “Step 1 — Build the specialist task agents”](#step-1--build-the-specialist-task-agents)
In **Agent Studio → Task Agents**, create one analyst per KPI family. Configure each with a **JSON** response so the handler gets a predictable object back. A single reusable output schema keeps every analyst interchangeable from the handler’s point of view:
```json
{
"type": "object",
"properties": {
"summary": { "type": "string", "description": "One-paragraph headline of what the data shows" },
"findings": {
"type": "array",
"description": "The most important observations, most significant first",
"items": {
"type": "object",
"properties": {
"metric": { "type": "string" },
"observation": { "type": "string" },
"impact": { "type": "string", "enum": ["high", "medium", "low"] }
},
"required": ["metric", "observation", "impact"],
"additionalProperties": false
}
},
"anomalies": {
"type": "array",
"description": "Values that look off and are worth a human check",
"items": {
"type": "object",
"properties": {
"metric": { "type": "string" },
"note": { "type": "string" }
},
"required": ["metric", "note"],
"additionalProperties": false
}
},
"recommendations": {
"type": "array",
"description": "Concrete next actions to move the chosen KPI",
"items": { "type": "string" }
},
"confidence": { "type": "string", "enum": ["high", "medium", "low"] }
},
"required": ["summary", "findings", "anomalies", "recommendations", "confidence"],
"additionalProperties": false
}
```
Give each analyst instructions scoped to its KPI. A starting set:
| Task agent (`api_name`) | Optimizes for | Instructions focus |
| :--------------------------- | :------------ | :------------------------------------------------------------------------ |
| `revenue_growth_analyzer` | Revenue | Revenue drivers by segment, expansion vs. new, ARPU, refund/discount drag |
| `conversion_funnel_analyzer` | Conversion | Stage-by-stage drop-off, activation, signup→paid, where to intervene |
| `retention_churn_analyzer` | Retention | Cohort retention, churn concentration, refunds, at-risk segments |
| `engagement_analyzer` | Engagement | Feature usage, active-user trends, engagement anomalies |
Example instructions for one analyst — keep them tight and tell it the input to expect and the goal to serve:
> You are a retention and churn analyst. You receive `report` (rows of metrics, often by cohort or segment) and a `question` describing the user’s goal. Identify where retention is strongest and weakest, concentrate on churn drivers and at-risk segments, flag anomalies, and return concrete actions to improve retention. Be specific and quantitative; never invent numbers not in the data.
Let the agent draft it
The Task Agent editor’s **Generate** button (✦) drafts the description and instructions from a plain-English brief. The Knowledge Base also feeds these analysts automatically — if you keep an article on *what good retention analysis looks like for our product*, the analyst applies it.
See the [Task Agents guide](/user-guides/task-agents/) for the editor, and [Example: analyze report data](/guides/task-agent-analysis/) for a worked single-analyst call.
## Step 2 — Author the routing action
[Section titled “Step 2 — Author the routing action”](#step-2--author-the-routing-action)
In **Agent Studio → Actions**, create `analyze_report_insights`. This is the front door; its job is to collect the goal/KPI and hand off.
**Description** (the agent reads this to decide *when* to call it — say what it returns):
> Analyzes a reporting or analytics report and returns structured insights — summary, key findings, anomalies, and recommended actions — tuned to the KPI the user wants to optimize.
**Instructions** (the qualifying-question rule — this is what makes it a *router*, not a guesser):
> Use when the user wants to analyze a report, dashboard, or analytics export and get insights or recommendations. **Before calling, make sure you know which goal or KPI to optimize for.** If the user hasn’t said, ask: *“What goal or KPI do you want to optimize — for example revenue, conversion, retention, or engagement?”* When the goal is already clear from context, skip the question and proceed. Pass the user’s objective in `goal` and the matching category in `kpi`.
**User Input** parameters (see [Defining inputs](/user-guides/defining-inputs/)):
| Parameter | Type | Required | Purpose |
| :-------- | :------------- | :------: | :----------------------------------------------------------------- |
| `report` | Object / Array | Yes | The report data (or a reference the handler can resolve) |
| `goal` | String | No | The user’s objective in their own words, e.g. “reduce churn in EU” |
| `kpi` | String (enum) | Yes | One of `revenue`, `conversion`, `retention`, `engagement`, `other` |
The `kpi` enum is what the handler routes on; `goal` is the free-text nuance the analyst reads.
## Step 3 — Write the `execute()` router handler
[Section titled “Step 3 — Write the execute() router handler”](#step-3--write-the-execute-router-handler)
The handler is small on purpose: map `kpi` to a task-agent key, call it, and return the structured result. All the reasoning lives in the analysts.
```javascript
// KPI → the specialist task agent that handles it.
// Add a KPI by adding a task agent and one entry here — nothing else changes.
const ROUTES = {
revenue: 'revenue_growth_analyzer',
conversion: 'conversion_funnel_analyzer',
retention: 'retention_churn_analyzer',
engagement: 'engagement_analyzer',
};
foldspace('when', 'ready', () => {
foldspace.agent({ /* …common setup… */ })
.addActionHandlers({
analyze_report_insights: {
// Analysis can take longer than the 5s default — give it room.
timeout: 60000,
execute: async ({ report, goal, kpi }) => {
const taskKey = ROUTES[kpi];
// Routing couldn't decide — hand control back so the agent asks the
// qualifying question instead of guessing an analyst.
if (!taskKey) {
return {
needsGoal: true,
message: 'Which KPI should I optimize for?',
options: Object.keys(ROUTES),
};
}
try {
const insights = await foldspace
.agent('YOUR-AGENT-API-NAME')
.runTask({
taskKey,
data: {
report,
goal,
question: `Optimize for ${goal || kpi}. Surface the drivers, flag anomalies, and recommend concrete next actions.`,
},
// Dashboards refresh often; reuse an identical analysis for an hour.
cacheOptions: { ttlSeconds: 3600 },
});
// Return the KPI alongside the analyst's output so the agent can
// frame its reply ("Here's what matters for retention…").
return { kpi, goal, ...insights };
} catch (error) {
console.error('Report analysis failed:', error);
return { error: true, kpi, message: 'The analysis could not be completed. Try again shortly.' };
}
},
},
});
});
```
A few things this handler gets right:
* **It routes, it doesn’t analyze.** Swapping a model or sharpening a prompt happens in the task agent, never here.
* **It closes the loop when routing fails.** The `needsGoal` return nudges the agent back to the qualifying question rather than picking an analyst at random.
* **It caches.** Identical `data` returns from cache, so a re-opened dashboard doesn’t pay twice — see [cache options](/reference/task-agent-api/#cache-options).
## Step 4 — The qualifying-question pattern, in depth
[Section titled “Step 4 — The qualifying-question pattern, in depth”](#step-4--the-qualifying-question-pattern-in-depth)
There are two ways the agent gathers the goal/KPI. Pick based on how much you want to steer:
1. **Conversationally (default, no code).** The action **Instructions** from Step 2 tell the agent to ask *“what goal or KPI do you want to optimize?”* whenever it’s unclear, and to skip the question when the goal is obvious from context. This is enough for most cases and keeps the flow natural.
2. **With an in-chat UI picker.** For a guided experience, render a KPI chooser in the chat from the action instead of returning plain data — see [UI components in chat](/guides/in-chat-ui/). Set `awaitUserInput: true` on the handler so the agent pauses for the user’s selection before the analysis runs.
The `needsGoal` fallback in the handler is the safety net for both: if the KPI still isn’t set when the action fires, control returns to the agent to ask, rather than to a wrong analyst.
## Step 5 — Test, evaluate, publish
[Section titled “Step 5 — Test, evaluate, publish”](#step-5--test-evaluate-publish)
* **Route correctly.** Use the action’s **Evals** tab to confirm real phrasings (“why are we losing customers?”, “where’s revenue coming from?”) extract the right `kpi` and route to the right analyst.
* **Analyze correctly.** Use each task agent’s **Evaluation** tab to assert the JSON output meets your criteria on representative reports before it ever reaches the main agent.
* **Watch cost and latency.** The task agent **Logs** tab records inputs, output, model, latency, and cost per run — use it to move simple analysts to a cheaper model.
* **Publish in order.** Publish the **task agents first** (the handler calls them by `taskKey`), then publish the **routing action**. A task agent must be Published before the agent can delegate to it.
## Best practices & extend it
[Section titled “Best practices & extend it”](#best-practices--extend-it)
* **One clear job per analyst.** If an analyst’s instructions need “and” several times, it’s probably two analysts.
* **Add a KPI in two edits.** Create the task agent, add one `ROUTES` entry. The action, its schema, and the handler are untouched.
* **Cache for dashboards, bypass on fresh data.** Set `cacheOptions.bypass: true` when the underlying report changed and you need a fresh run.
* **Observe adoption.** Track execution and routing accuracy with Action Insights and [Conversational Analytics](/user-guides/analytics/dashboard/) — they tell you which KPIs users actually ask about, so you know which analyst to build next.
## Related
[Section titled “Related”](#related)
* [Task agents](/user-guides/task-agents/) — configure the specialist sub-agents.
* [Example: analyze report data](/guides/task-agent-analysis/) — a single-analyst `runTask()` call.
* [Authoring actions](/user-guides/authoring-actions/) · [Execute actions](/guides/executing-actions/) — the two halves of the routing action.
* [UI components in chat](/guides/in-chat-ui/) — render a KPI picker for the qualifying question.
# Appearance and greeting
> Set your agent's name, logo, entry message, and prompt bar hint to match your brand.
These settings control how the agent looks and how it introduces itself. Pass them through `setConfiguration` as described in the [SDK configuration](/customize/configuration/) reference.
## Options
[Section titled “Options”](#options)
| Key | Type | Default | Description |
| :----------------- | :-------- | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
| `agentDisplayName` | `string` | `"Agent"` | The display name shown for the agent in the chat interface. |
| `entryMessage` | `string` | `"How can I help you today?"` | The first message users see when the chat interface launches. Sets the initial tone of the conversation. |
| `logoUrl` | `string` | `""` | URL of the logo displayed in the agent interface. |
| `promptBarHint` | `string` | `""` | Placeholder text inside the prompt input bar, used to guide the user on what to ask (for example, `"Ask me anything..."`). |
| `displaySparkIcon` | `boolean` | `true` | Whether the spark icon is visible in the agent interface. |
## Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
agentDisplayName: "Foldspace Assistant",
entryMessage: "What can I help with?",
logoUrl: "https://assets.foldspace.com/logos/foldspace-logo-light.svg",
promptBarHint: "Ask me anything...",
displaySparkIcon: true
});
```
To set colors and typography alongside these settings, see [Theme](/customize/theme/).
# Border Animation Settings
> Defines animated border effects for highlighting the agent.
## enableBorderAnimation
[Section titled “enableBorderAnimation”](#enableborderanimation)
Determines whether animated border effects are enabled around the chat interface or agent widget. When active, borders smoothly transition between defined colors.
* **Type:** `boolean`
* **Default Value:** `true`
## borderColors
[Section titled “borderColors”](#bordercolors)
Defines the sequence of colors used for the border animation. The colors transition in order to create a dynamic visual effect.
* **Type:** `array`
* **Default Value:** `["#0054B6", "#ffffff"]`
## Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
// Border animation settings
borderAnimationSettings: {
enableBorderAnimation: true,
borderColors: ["#0066FF", "#3B82F6", "#60A5FA"]
}
});
```
# SDK configuration
> Configure how your Foldspace agent looks, behaves, and renders through the SDK.
Prerequisites
The [SDK is installed](/start/install/) on your page.
The SDK accepts a single configuration object that controls how the agent looks, introduces itself, and behaves on the page. Pass it through `setConfiguration` on an agent instance:
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
// settings go here
});
```
```
flowchart TD
SC["setConfiguration({ })"]
AP["Appearance & greeting name, logo, entry message"]
PL["Placement & rendering position, render mode, drag"]
TH["Theme colors, fonts, border"]
DM["Dark mode dark theme, dark logo"]
IC["Interaction timeout, voice, speech"]
CS["Conversation starters prompts, defaults"]
SO["Style overrides CSS-level customization"]
SC --> AP
SC --> PL
SC --> TH
SC --> DM
SC --> IC
SC --> CS
SC --> SO
```
Settings are grouped into focused areas. Each grouped setting (such as `theme`) has its own reference page covering every key, its type, and its default value.
## Setting groups
[Section titled “Setting groups”](#setting-groups)
| Group | What it controls |
| :----------------------------------------------------------- | :-------------------------------------------------------------------------- |
| [Appearance and greeting](/customize/appearance/) | The agent’s display name, logo, entry message, and prompt bar hint. |
| [Placement and rendering](/customize/placement/) | How the agent is embedded, anchored, and positioned on screen. |
| [Theme](/customize/theme/) | The colors and typography used in the agent interface. |
| [Dark mode](/customize/dark-mode/) | Dark-theme colors and a dark-specific logo. |
| [Interaction and communication](/customize/interaction/) | Session timeout, voice chat, speech-to-text, and new-conversation triggers. |
| [Conversation starters](/customize/conversation-starters/) | Predefined prompts and behavior for starting conversations. |
| [Style overrides](/customize/style-overrides/) | CSS-level overrides for the entry button, speech bubble, and agent frame. |
| [Border animation](/customize/border-animation/) | The animated border around the agent entry point. |
| [Share current tab](/customize/share-current-tab/) | Let the agent read and understand the current page content. |
| [Embedded configuration](/customize/embedded-configuration/) | Render the agent inline inside a specific DOM container. |
| [Localization](/customize/localization/) | Language override and developer-translated strings. |
See also the [full configuration example](/customize/configuration-example/) for a single snippet that uses every group together.
## Studio and SDK precedence
[Section titled “Studio and SDK precedence”](#studio-and-sdk-precedence)
```
flowchart TD
ST["Studio settings dashboard UI"] --> D["Default values"]
SDK["SDK setConfiguration code"] --> D
SDK -->|"overrides"| ST
D --> R["Rendered agent"]
style SDK fill:#1842ef,stroke:#1842ef,color:#ffffff
style R fill:#e8ecff,stroke:#1842ef,color:#0a0e1a
```
Configure every one of these settings visually in [Studio](https://app.foldspace.ai/agent/customize). SDK configuration takes precedence over Studio values, so you can override defaults per page or per context directly in code.
# Agent Configuration Example
> An example showcasing how to customize your Foldspace Agent's look and behavior.
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
// String properties
agentDisplayName: "Foldspace Assistant",
entryMessage: "What can I help with?",
logoUrl: "https://assets.foldspace.com/logos/foldspace-logo-light.svg",
renderMode: "IFRAME",
promptBarHint: "Ask me anything...",
// Boolean properties
allowAgentDrag: true,
enableVoiceChat: true,
enableSpeechToText: true,
displaySparkIcon: true,
// Position property (format: "VERTICAL_HORIZONTAL")
initialAgentPosition: "BOTTOM_RIGHT",
// Session timeout in minutes
sessionTimeout: 30,
// Theme object
theme: {
primaryColor: "#0066FF",
primaryFontColor: "#0B1220",
backgroundColor: "#FFFFFF",
fontFamily: "Roboto",
fontSize: 14,
secondaryFontColor: "#6B7280",
secondaryColor: "#F3F4F6",
borderColor: "#E6E9EE"
},
// Dark mode settings
darkModeSettings: {
darkTheme: {
primaryColor: "#3B82F6",
primaryFontColor: "#FFFFFF",
backgroundColor: "#0F1724",
fontFamily: "Roboto",
fontSize: 14,
secondaryFontColor: "#9CA3AF",
secondaryColor: "#111827",
borderColor: "#1F2937"
},
darkLogoUrl: "https://assets.foldspace.com/logos/foldspace-logo-dark.svg"
},
// Border animation settings
borderAnimationSettings: {
enableBorderAnimation: true,
borderColors: ["#0066FF", "#3B82F6", "#60A5FA"]
},
// Conversation starters settings
conversationStartersSettings: {
defaultConversationStarter: "KNOWLEDGE",
startNewConversationOnStarterClick: true,
conversationStarters: {
KNOWLEDGE: [
{ title: "What is your return policy?" },
{ title: "How do I track my order?" },
{ title: "Show me product specifications" }
],
ACTION: [
{ title: "Schedule a demo" },
{ title: "Check my order status" },
{ title: "Contact support" }
]
}
},
// Style override
styleOverride: {
entryComponentStyle: {
header: {
horizontalAlignment: "start"
},
conversationStarter: {
borderColor: "#E6E9EE",
borderWidth: 1
}
},
speechBubbleStyle: {
padding: "14px 16px",
borderRadius: "12px",
backgroundColor: "#FFFFFF",
color: "#0B1220",
fontSize: "14px",
lineHeight: "1.4"
}
},
shareStateSettings: {
active: true,
tooltipText: "Enable Tandem Mode — allow the assistant to read the page to help complete tasks",
tooltipStarters: [
"Fill out this form for me",
"Suggest values for the fields on this page"
]
},
screenSharingSettings: {
active: true,
tooltipText: "Allow the assistant to understand this page and provide helpful suggestions",
tooltipStarters: [
"Summarize this page for me",
"Highlight important information here"
],
shareMode: "both"
}
});
```
# Conversation starters
> Configure the KNOWLEDGE and ACTION prompts shown on the agent entry screen, and override them at runtime with the programmatic API.
Conversation starters are the predefined prompts shown on the agent entry screen. Configure them statically with `setConfiguration`, or override them at runtime with the programmatic API.
To translate starters for different locales, see [Localization](/customize/localization/).
## conversationStarters
[Section titled “conversationStarters”](#conversationstarters)
The set of starter prompts shown to the user. This is an object with two arrays, `KNOWLEDGE` and `ACTION`, each representing a distinct type of starter:
* **KNOWLEDGE**: informational or contextual questions based on existing knowledge.
* **ACTION**: task-oriented starters that trigger specific actions.
Each item in either array must follow the format `{ title: "" }`.
Type: `object`\
Default value: `{ "KNOWLEDGE": [], "ACTION": [] }`
## defaultConversationStarter
[Section titled “defaultConversationStarter”](#defaultconversationstarter)
Which starter group is shown by default when the chat loads.
Type: `string`\
Default value: `"KNOWLEDGE"`\
Accepted values: `"KNOWLEDGE" | "ACTION"`
## startNewConversationOnStarterClick
[Section titled “startNewConversationOnStarterClick”](#startnewconversationonstarterclick)
Whether clicking a starter begins a new conversation instead of continuing the current one.
Type: `boolean`\
Default value: `false`
## Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
conversationStartersSettings: {
defaultConversationStarter: "KNOWLEDGE",
startNewConversationOnStarterClick: true,
conversationStarters: {
KNOWLEDGE: [
{ title: "What is your return policy?" },
{ title: "How do I track my order?" },
{ title: "Show me product specifications" }
],
ACTION: [
{ title: "Schedule a demo" },
{ title: "Check my order status" },
{ title: "Contact support" }
]
}
}
});
```
## Programmatic API
[Section titled “Programmatic API”](#programmatic-api)
### `setConversationStarters(starters, defaultStarterType?)`
[Section titled “setConversationStarters(starters, defaultStarterType?)”](#setconversationstartersstarters-defaultstartertype)
Overrides the conversation starters shown on the entry screen at runtime. If called before the agent is ready, the call is queued and replayed once the agent initializes.
**Parameters:**
| Parameter | Type | Required | Description |
| -------------------- | ------------------------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `starters` | `Record<"KNOWLEDGE" \| "ACTION", { title: string }[]>` | Yes | A record with `KNOWLEDGE` and `ACTION` arrays. Pass `null` to restore the original remote configuration starters. |
| `defaultStarterType` | `"KNOWLEDGE" \| "ACTION"` | No | Switch which starter tab is shown by default. If omitted or `null`, the original remote default is restored. |
**Returns:** the agent instance, enabling method chaining.
**Example: override starters**
```javascript
const agent = foldspace.agent('YOUR-AGENT-KEY');
agent.setConversationStarters(
{
KNOWLEDGE: [
{ title: "What is your return policy?" },
{ title: "How do I track my order?" }
],
ACTION: [
{ title: "Schedule a demo" },
{ title: "Contact support" }
]
},
"ACTION"
);
```
**Example: restore original starters from remote config**
```javascript
agent.setConversationStarters(null);
```
### `getConversationStarters()`
[Section titled “getConversationStarters()”](#getconversationstarters)
Returns the current conversation starters and the default starter type. If starters were overridden via `setConversationStarters`, the overridden values are returned. Otherwise, it reflects the remote configuration.
**Parameters:** none.
**Returns:** `{ starters, defaultStarterType } | null`
Returns `null` if the agent is not yet initialized.
| Field | Type | Description |
| -------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| `starters` | `Record<"KNOWLEDGE" \| "ACTION", { title: string }[]>` | The starters grouped by type. Each group is guaranteed to contain at least one entry. |
| `defaultStarterType` | `"KNOWLEDGE" \| "ACTION"` | Which starter tab is currently the default. |
**Example:**
```javascript
const result = agent.getConversationStarters();
console.log(result);
```
**Response:**
```json
{
"starters": {
"KNOWLEDGE": [
{ "title": "What is your return policy?" },
{ "title": "How do I track my order?" }
],
"ACTION": [
{ "title": "Schedule a demo" },
{ "title": "Contact support" }
]
},
"defaultStarterType": "ACTION"
}
```
# Dark mode
> Configure a dark theme and a dark-mode logo so your agent stays on-brand across light and dark appearances.
Supply theme overrides and a dedicated logo that apply when the agent renders in dark mode. Pass a `darkModeSettings` object to `setConfiguration`.
To override individual component styles instead of the whole theme, see [Style Overrides](/customize/style-overrides/).
## darkLogoUrl
[Section titled “darkLogoUrl”](#darklogourl)
URL of the logo shown when dark mode is active. Use it to keep branding legible against a dark background.
Type: `string`\
Default value: `""`
## darkTheme
[Section titled “darkTheme”](#darktheme)
Theme overrides applied in dark mode. Its structure mirrors the standard [Theme](/customize/theme/) settings, so you control colors and styles in the dark appearance.
Type: `object`\
Default value: `{}`
```
flowchart TD
T["Theme settings primaryColor, fontFamily, ..."] --> L["Light mode"]
DT["darkModeSettings.darkTheme overrides same keys"] --> D["Dark mode"]
T -.->|"inherits unset keys"| D
DL["darkLogoUrl"] --> D
style L fill:#ffffff,stroke:#e2e4ea,color:#0a0e1a
style D fill:#0a0e1a,stroke:#1d2336,color:#e2e4ea
```
## Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
// Dark mode settings
darkModeSettings: {
darkTheme: {
primaryColor: "#3B82F6",
primaryFontColor: "#FFFFFF",
backgroundColor: "#0F1724",
fontFamily: "Roboto",
fontSize: 14,
secondaryFontColor: "#9CA3AF",
secondaryColor: "#111827",
borderColor: "#1F2937"
},
darkLogoUrl: "https://assets.foldspace.com/logos/foldspace-logo-dark.svg"
}
});
```
# Embedded Mode Configuration
> Configure how the agent mounts directly into a specific DOM element within your application layout.
Embedded mode allows you to mount the agent directly into a specific DOM element within your application layout. This is ideal for creating integrated chat experiences, custom sidebars, or dedicated support pages where the agent is a native part of the UI rather than a floating widget.
## embeddedConfiguration
[Section titled “embeddedConfiguration”](#embeddedconfiguration)
Defines the layout and behavior of the agent when mode is set to `EMBEDDED`. This object controls how the agent mounts into your application’s DOM and its initial UI state.
**Structure:**
* **Container:** The HTML element into which the agent will mount.
* **Type:** `HTMLElement`
## Example
[Section titled “Example”](#example)
```javascript
const containerElement = document.getElementById('agent-container');
window.foldspace.agent({
apiName: 'AGENT_API_NAME',
mode: 'EMBEDDED',
configuration: {
embeddedConfiguration: {
container: containerElement,
}
}
});
```
# Interaction and communication
> Enable voice conversations and speech-to-text input so users can talk to your agent instead of typing.
These settings control how users communicate with the agent: by typing, by speaking, or both. Pass these flags to `setConfiguration`.
## enableVoiceChat
[Section titled “enableVoiceChat”](#enablevoicechat)
Lets users hold a real-time voice conversation with the agent instead of typing.
Type: `boolean`\
Default value: `false`
## enableSpeechToText
[Section titled “enableSpeechToText”](#enablespeechtotext)
Adds a microphone button to the chat interface. When a user clicks the mic, their speech is recorded and transcribed into a text prompt.
Type: `boolean`\
Default value: `false`
## Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
enableVoiceChat: true,
enableSpeechToText: true,
});
```
# Localization
> Serve your agent in your user's language with languageOverride, and translate the strings the server does not handle for you.
Pass `languageOverride` when initializing the agent to fetch its configuration in a specific language. The SDK sends the language code to the server, which returns translated content for that locale.
```javascript
foldspace.agent('YOUR-AGENT-KEY', {
languageOverride: "fr"
});
```
The value must be an **ISO 639-1** language code, such as `"en"`, `"fr"`, `"es"`, `"de"`, or `"ja"`.
## What gets translated automatically
[Section titled “What gets translated automatically”](#what-gets-translated-automatically)
When `languageOverride` is set, the server returns the agent configuration with **conversation starters** translated into the requested language, provided those translations have been set up in advance (see [Translating conversation starters in the admin UI](#translating-conversation-starters-in-the-admin-ui)).
## What requires developer translation
[Section titled “What requires developer translation”](#what-requires-developer-translation)
The following UI strings are **not** translated automatically by the server. If your application supports multiple languages, you are responsible for translating them and passing the correct values via `setConfiguration`:
| String | Config property | Where it appears |
| --------------------- | ---------------------- | ------------------------------------------ |
| Entry Message | `entryMessage` | Placeholder text inside the chat input bar |
| Agent name | `agentDisplayName` | Header of the agent panel |
| Conversation starters | `conversationStarters` | Entry screen prompts shown to the user |
For the full conversation starter reference, see [Conversation Starters](/customize/conversation-starters/).
### Example: translating dynamic strings
[Section titled “Example: translating dynamic strings”](#example-translating-dynamic-strings)
```javascript
const translations = {
en: {
agentDisplayName: "Support Agent",
entryMessage: "Ask me anything...",
starters: {
KNOWLEDGE: [
{ title: "What is your return policy?" },
{ title: "How do I track my order?" }
],
ACTION: [
{ title: "Schedule a demo" },
{ title: "Contact support" }
]
}
},
fr: {
agentDisplayName: "Agent d'assistance",
entryMessage: "Posez-moi une question...",
starters: {
KNOWLEDGE: [
{ title: "Quelle est votre politique de retour ?" },
{ title: "Comment suivre ma commande ?" }
],
ACTION: [
{ title: "Planifier une démo" },
{ title: "Contacter le support" }
]
}
}
};
const userLang = "fr";
const t = translations[userLang];
const agent = foldspace.agent('YOUR-AGENT-KEY', {
languageOverride: userLang
});
agent.setConfiguration({
agentDisplayName: t.agentDisplayName,
entryMessage: t.entryMessage
});
agent.setConversationStarters(t.starters);
```
## Translating conversation starters in the admin UI
[Section titled “Translating conversation starters in the admin UI”](#translating-conversation-starters-in-the-admin-ui)
You can translate conversation starters directly from the Foldspace admin panel without writing any code. Go to your agent’s **Starters** page under **Customize** and add translations for each language. Translations added there are served automatically when the SDK requests a specific language via `languageOverride`, so no additional SDK code is needed for starters managed through the admin UI.
## Fallback behavior
[Section titled “Fallback behavior”](#fallback-behavior)
If the requested language is not available, the agent falls back to **English (`en`)**. Two console warnings help you identify translation issues:
* `[Foldspace] Requested language "xx" is not supported. Serving "en" instead.` The server did not have translations for the requested language and returned English.
* `[Foldspace] Requested language "xx" — server did not confirm language resolution. Translations may not be available.` The server did not return an `X-Resolved-Language` header, so the response may or may not be translated.
If `languageOverride` is omitted, the agent defaults to English.
# Placement and rendering
> Control how your Foldspace agent is embedded, positioned, and behaves on screen.
These settings control how the agent is embedded into the host page, where it appears, and how it behaves on screen. Pass them through `setConfiguration` as described in the [SDK configuration](/customize/configuration/) reference.
## Options
[Section titled “Options”](#options)
| Key | Type | Default | Accepted values | Description |
| :--------------------- | :-------- | :--------------- | :------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------- |
| `allowAgentDrag` | `boolean` | `true` | | Whether users can click and drag the agent widget to reposition it anywhere on the screen. |
| `renderMode` | `string` | `"IFRAME"` | `"IFRAME" \| "INLINE"` | How the SDK is embedded within the host page: as an iframe or an inline component. |
| `sessionTimeout` | `number` | `30` | `5` to `1440` | The maximum idle time (in minutes) before a new conversation is automatically started. Minimum 5 minutes, maximum 1440 minutes (24 hours). |
| `initialAgentPosition` | `string` | `"CENTER_RIGHT"` | `"TOP_RIGHT" \| "TOP_LEFT" \| "BOTTOM_RIGHT" \| "BOTTOM_LEFT" \| "CENTER_RIGHT" \| "CENTER_LEFT"` | The default on-screen location where the agent widget appears when first loaded. |
Note
`initialAgentPosition` values follow the format `"VERTICAL_HORIZONTAL"`.
## Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
renderMode: "IFRAME",
allowAgentDrag: true,
// Position property (format: "VERTICAL_HORIZONTAL")
initialAgentPosition: "BOTTOM_RIGHT",
// Session timeout in minutes
sessionTimeout: 30,
});
```
# Share current tab
> Give the agent real-time page context — configured in the dashboard, with an SDK override for programmatic control.
Share current tab lets the agent read and interpret the page’s content, giving it full awareness of the page’s structure and elements so it can guide users based on what’s on the page. This is the **page context** the agent reasons over on each turn (see [Context](/guides/context/)).
Turn it on and tune it from your agent’s settings in the **dashboard** — that’s the primary way to configure it. The SDK settings below are an **override**: use `setConfiguration({ screenSharingSettings })` to control it programmatically, per page or per session, on top of the dashboard defaults.
## SDK override
[Section titled “SDK override”](#sdk-override)
### active
[Section titled “active”](#active)
Enables or disables the Shared Current Tab feature. When `true`, the agent can read and understand the content of the current page to provide contextual assistance, suggest actions, or highlight relevant information without making any changes directly.
* **Type:** `boolean`
* **Default Value:** `false`
### tooltipText
[Section titled “tooltipText”](#tooltiptext)
One-line hover label shown on the Agent control. Use this to clearly explain what enabling Shared Current Tab does in a short, non-technical way that reassures users about control and privacy.
* **Type:** `string`
* **Default Value:** `"Let the assistant understand this page and help you."`
### tooltipStarters
[Section titled “tooltipStarters”](#tooltipstarters)
Array of up to 2 short suggested prompts shown in the UI to help users quickly start interactions using Shared Current Tab. Hints should be concise, actionable, and reassure users that the agent only reads the page to assist, without modifying it.
* **Type:** `array`
* **Default Value:** `[ "Summarize this", "Explain what I'm seeing" ]`
### shareMode
[Section titled “shareMode”](#sharemode)
Determines what type of data the SDK includes when the user shares their screen with the agent. This controls whether the agent receives only an image of the page, only its HTML structure, or both.
* **Type:** `"image" | "html" | "both"`
* **Default Value:** `"both"`
### Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
screenSharingSettings: {
active: true,
tooltipText: "Allow the assistant to understand this page and provide helpful suggestions",
tooltipStarters: [
"Summarize this page for me",
"Highlight important information here"
],
shareMode: "both"
}
});
```
# Style overrides
> Override the styles of individual SDK components, including the entry component and user message speech bubbles.
Restyle individual agent components without rebuilding the theme. Pass a `styleOverride` object to `setConfiguration` to control layout, borders, color, and typography on specific components.
For global theming, see [Dark Mode](/customize/dark-mode/).
## entryComponentStyle
[Section titled “entryComponentStyle”](#entrycomponentstyle)
Styles the entry component, including its header layout and the conversation starter borders. This is an object with nested properties for each area of the entry component.
Type: `object`
Default value:
```javascript
{
"header": {
"horizontalAlignment": "start"
},
"conversationStarter": {
"borderWidth": 1,
"borderColor": "#000000"
}
}
```
### Properties
[Section titled “Properties”](#properties)
| Property | Type | Default | Description |
| --------------------------------- | ------------------------------ | ---------- | ------------------------------------------ |
| `header.horizontalAlignment` | `"start" \| "center" \| "end"` | `"center"` | Horizontal alignment of header content. |
| `conversationStarter.borderWidth` | `number` | `1` | Border width of each conversation starter. |
| `conversationStarter.borderColor` | `string` | `""` | Border color of each conversation starter. |
## speechBubbleStyle
[Section titled “speechBubbleStyle”](#speechbubblestyle)
Styles the user message speech bubble. Use it to control spacing, color, and typography inside the bubble.
Type: `object`\
Default value: `{}`
### Properties
[Section titled “Properties”](#properties-1)
| Property | Type | Description |
| ----------------- | -------- | ------------------------------------------ |
| `padding` | `string` | Internal spacing inside the speech bubble. |
| `borderRadius` | `string` | Corner rounding of the speech bubble. |
| `backgroundColor` | `string` | Background color of the speech bubble. |
| `color` | `string` | Text color inside the speech bubble. |
| `fontSize` | `string` | Font size for bubble text. |
| `lineHeight` | `string` | Line height for text inside the bubble. |
## Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
styleOverride: {
entryComponentStyle: {
header: {
horizontalAlignment: "start"
},
conversationStarter: {
borderColor: "#E6E9EE",
borderWidth: 1
}
},
speechBubbleStyle: {
padding: "14px 16px",
borderRadius: "12px",
backgroundColor: "#FFFFFF",
color: "#0B1220",
fontSize: "14px",
lineHeight: "1.4"
}
}
});
```
# Theme
> Define the colors and typography used across your Foldspace agent interface.
The `theme` group controls the colors and typography used across the agent interface. Pass it as a nested object inside `setConfiguration`, as described in the [SDK configuration](/customize/configuration/) reference.
## Options
[Section titled “Options”](#options)
| Key | Type | Default | Description |
| :------------------- | :------- | :---------- | :-------------------------------------------------------------------------------------------- |
| `primaryColor` | `string` | `"#0054B6"` | The main accent color used for key interactive and highlight elements across the UI. |
| `primaryFontColor` | `string` | `"#121926"` | The main text color applied to primary content and key interface elements. |
| `backgroundColor` | `string` | `"#FFFFFF"` | The primary background color for chat containers and other main interface components. |
| `fontFamily` | `string` | `"Poppins"` | The typeface used throughout the interface. |
| `fontSize` | `number` | `14` | The default font size for text across the interface. |
| `borderColor` | `string` | `""` | The color used for borders around static UI elements, such as containers or input fields. |
| `secondaryFontColor` | `string` | `"#121926"` | The text color for secondary content, such as hints, timestamps, or less prominent labels. |
| `secondaryColor` | `string` | `"#0054B6"` | The secondary accent color, used to complement the primary color and create visual hierarchy. |
## Example
[Section titled “Example”](#example)
```javascript
foldspace.agent('YOUR-AGENT-KEY').setConfiguration({
theme: {
primaryColor: "#0066FF",
primaryFontColor: "#0B1220",
backgroundColor: "#FFFFFF",
fontFamily: "Roboto",
fontSize: 14,
secondaryFontColor: "#6B7280",
secondaryColor: "#F3F4F6",
borderColor: "#E6E9EE"
}
});
```
# A2A advanced patterns
> LangGraph, TypeScript, Java, and native (no-SDK) implementations of an A2A server agent, plus streaming progress and multi-turn user input.
This page covers building A2A server agents beyond the [Python quickstart](/guides/a2a-quickstart/): orchestrating with [LangGraph](https://www.langchain.com/langgraph), the TypeScript and Java SDKs, and implementing the protocol natively without an SDK.
## LangGraph integration
[Section titled “LangGraph integration”](#langgraph-integration)
[LangGraph](https://langchain-ai.github.io/langgraph/) provides a graph-based orchestration layer for building stateful, multi-step agents. If you already orchestrate agents with LangGraph, you can expose a compiled graph as an A2A server agent with a thin `AgentExecutor` bridge — no need to rewrite your agent logic.
> New to LangGraph? See the [official LangGraph documentation](https://langchain-ai.github.io/langgraph/) and the [`langchain-ai/langgraph` repository](https://github.com/langchain-ai/langgraph).
### Basic LangGraph agent
[Section titled “Basic LangGraph agent”](#basic-langgraph-agent)
Wrap a LangGraph research agent using the `AgentExecutor` bridge pattern to expose it as an A2A server.
research\_agent.py
```python
import uvicorn
from langgraph.graph import StateGraph, MessagesState, START, END
from langchain_openai import ChatOpenAI
from a2a.server.apps.jsonrpc import A2AStarletteApplication
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
from a2a.types import (
AgentCard,
AgentCapabilities,
AgentSkill,
Part,
TextPart,
)
# --- Step 1: Define the LangGraph agent ---
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
def research_node(state: MessagesState):
response = llm.invoke(state["messages"])
return {"messages": [response]}
graph_builder = StateGraph(MessagesState)
graph_builder.add_node("researcher", research_node)
graph_builder.add_edge(START, "researcher")
graph_builder.add_edge("researcher", END)
research_graph = graph_builder.compile()
# --- Step 2: Bridge LangGraph to A2A ---
class ResearchAgentExecutor(AgentExecutor):
def __init__(self, graph):
self.graph = graph
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.start_work()
user_input = context.get_user_input()
result = await self.graph.ainvoke(
{"messages": [{"role": "user", "content": user_input}]}
)
response_text = result["messages"][-1].content
await updater.add_artifact(parts=[Part(root=TextPart(text=response_text))])
await updater.complete()
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.cancel()
# --- Step 3: Agent Card + Server ---
agent_card = AgentCard(
name="Research Assistant Agent",
description="Researches topics and provides detailed, cited answers",
url="http://localhost:9000/",
version="1.0.0",
capabilities=AgentCapabilities(streaming=True),
default_input_modes=["text/plain"],
default_output_modes=["text/plain"],
skills=[
AgentSkill(
id="research",
name="Deep Research",
description="Researches any topic and provides comprehensive answers",
tags=["research", "knowledge", "analysis"],
examples=["What are the key differences between gRPC and REST?"],
),
],
)
request_handler = DefaultRequestHandler(
agent_executor=ResearchAgentExecutor(research_graph),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=agent_card,
http_handler=request_handler,
)
if __name__ == "__main__":
uvicorn.run(server.build(), host="0.0.0.0", port=9000)
```
### LangGraph with streaming progress
[Section titled “LangGraph with streaming progress”](#langgraph-with-streaming-progress)
For long-running agents, stream intermediate updates with `astream_events` so users see progress in real time.
```python
class StreamingResearchExecutor(AgentExecutor):
def __init__(self, graph):
self.graph = graph
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.start_work()
user_input = context.get_user_input()
async for event in self.graph.astream_events(
{"messages": [{"role": "user", "content": user_input}]},
version="v2",
):
if event["event"] == "on_chat_model_stream":
chunk = event["data"]["chunk"].content
if chunk:
await updater.update_status(
state="working",
message=Part(root=TextPart(text=chunk)),
)
final_result = await self.graph.ainvoke(
{"messages": [{"role": "user", "content": user_input}]}
)
response_text = final_result["messages"][-1].content
await updater.add_artifact(parts=[Part(root=TextPart(text=response_text))])
await updater.complete()
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.cancel()
```
### LangGraph with multi-turn (user input required)
[Section titled “LangGraph with multi-turn (user input required)”](#langgraph-with-multi-turn-user-input-required)
When your agent needs clarification before proceeding, transition to `input-required` — the Copilot prompts the user and relays their reply back.
```python
class MultiTurnResearchExecutor(AgentExecutor):
def __init__(self, graph):
self.graph = graph
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.start_work()
user_input = context.get_user_input()
result = await self.graph.ainvoke(
{"messages": [{"role": "user", "content": user_input}]}
)
response_text = result["messages"][-1].content
if result.get("needs_clarification", False):
await updater.update_status(
state="input-required",
message=Part(root=TextPart(text=response_text)),
)
else:
await updater.add_artifact(parts=[Part(root=TextPart(text=response_text))])
await updater.complete()
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.cancel()
```
## TypeScript SDK
[Section titled “TypeScript SDK”](#typescript-sdk)
Install the SDK and Express:
```bash
npm install @a2a-js/sdk express
```
A complete agent implementation demonstrating message handling, custom execution logic, and skill definitions.
agent.ts
```typescript
import express from 'express';
import { v4 as uuidv4 } from 'uuid';
import { AgentCard, Message } from '@a2a-js/sdk';
import {
AgentExecutor,
RequestContext,
ExecutionEventBus,
DefaultRequestHandler,
InMemoryTaskStore,
} from '@a2a-js/sdk/server';
import {
agentCardHandler,
jsonRpcHandler,
restHandler,
} from '@a2a-js/sdk/server/express';
class DocumentAnalysisExecutor implements AgentExecutor {
async execute(
requestContext: RequestContext,
eventBus: ExecutionEventBus
): Promise {
const userMessage = requestContext.message;
const inputText = userMessage.parts
.filter((p) => p.kind === 'text')
.map((p) => p.text)
.join(' ');
const analysisResult = await this.analyzeDocument(inputText);
const responseMessage: Message = {
kind: 'message',
messageId: uuidv4(),
role: 'agent',
parts: [{ kind: 'text', text: analysisResult }],
contextId: requestContext.contextId,
};
eventBus.publish(responseMessage);
eventBus.finished();
}
cancelTask = async (): Promise => {};
private async analyzeDocument(text: string): Promise {
return `Analysis complete. Document contains ${text.split(' ').length} words. Key topics identified.`;
}
}
const agentCard: AgentCard = {
name: 'Document Analysis Agent',
description: 'Analyzes documents for key topics, entities, and summaries',
url: 'http://localhost:3000',
version: '1.0.0',
capabilities: { streaming: true, pushNotifications: false },
defaultInputModes: ['text/plain', 'application/pdf'],
defaultOutputModes: ['text/plain', 'application/json'],
skills: [
{
id: 'summarize',
name: 'Summarize Document',
description: 'Produces a concise summary of the input document',
tags: ['nlp', 'summarization'],
examples: ['Summarize this contract for key obligations'],
},
{
id: 'extract-entities',
name: 'Extract Entities',
description: 'Identifies people, organizations, dates, and amounts',
tags: ['nlp', 'ner', 'extraction'],
examples: ['Extract all company names and dates from this filing'],
},
],
};
const handler = new DefaultRequestHandler({
agentExecutor: new DocumentAnalysisExecutor(),
taskStore: new InMemoryTaskStore(),
});
const app = express();
app.use(express.json());
app.get('/.well-known/agent.json', agentCardHandler(agentCard));
app.post('/jsonrpc', jsonRpcHandler(handler));
app.use('/api', restHandler(handler));
app.listen(3000, () => {
console.log('Document Analysis Agent running on port 3000');
});
```
## Java SDK
[Section titled “Java SDK”](#java-sdk)
Maven dependency:
```xml
org.a2aproject.sdka2a-java-sdk-reference-jsonrpc1.0.0.Beta1
```
A CDI producer configures the agent metadata — capabilities, skills, and supported interfaces:
```java
import org.a2aproject.sdk.server.PublicAgentCard;
import org.a2aproject.sdk.spec.AgentCapabilities;
import org.a2aproject.sdk.spec.AgentCard;
import org.a2aproject.sdk.spec.AgentInterface;
import org.a2aproject.sdk.spec.AgentSkill;
import org.a2aproject.sdk.spec.TransportProtocol;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import java.util.Collections;
import java.util.List;
@ApplicationScoped
public class CodeReviewAgentCardProducer {
private static final String AGENT_URL = "http://localhost:10001";
@Produces
@PublicAgentCard
public AgentCard agentCard() {
return AgentCard.builder()
.name("Code Review Agent")
.description("Reviews code for bugs, security issues, and style violations")
.supportedInterfaces(List.of(
new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL)))
.version("1.0.0")
.capabilities(AgentCapabilities.builder()
.streaming(true)
.pushNotifications(false)
.build())
.defaultInputModes(Collections.singletonList("text/plain"))
.defaultOutputModes(List.of("text/plain", "application/json"))
.skills(List.of(
AgentSkill.builder()
.id("review-code")
.name("Review Code")
.description("Analyzes code for bugs, security vulnerabilities, and improvements")
.tags(List.of("code-review", "security", "quality"))
.examples(List.of("Review this Java class for thread safety issues"))
.build(),
AgentSkill.builder()
.id("suggest-refactor")
.name("Suggest Refactoring")
.description("Proposes structural improvements and design patterns")
.tags(List.of("refactoring", "design-patterns"))
.examples(List.of("Suggest how to refactor this service class"))
.build()))
.build();
}
}
```
An executor processes requests and manages the task lifecycle, including cancellation:
```java
import org.a2aproject.sdk.server.agentexecution.AgentExecutor;
import org.a2aproject.sdk.server.agentexecution.RequestContext;
import org.a2aproject.sdk.server.tasks.AgentEmitter;
import org.a2aproject.sdk.spec.JSONRPCError;
import org.a2aproject.sdk.spec.Message;
import org.a2aproject.sdk.spec.Part;
import org.a2aproject.sdk.spec.Task;
import org.a2aproject.sdk.spec.TaskNotCancelableError;
import org.a2aproject.sdk.spec.TaskState;
import org.a2aproject.sdk.spec.TextPart;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import java.util.List;
@ApplicationScoped
public class CodeReviewExecutorProducer {
@Inject
CodeReviewAgent codeReviewAgent;
@Produces
public AgentExecutor agentExecutor() {
return new CodeReviewAgentExecutor(codeReviewAgent);
}
private static class CodeReviewAgentExecutor implements AgentExecutor {
private final CodeReviewAgent codeReviewAgent;
public CodeReviewAgentExecutor(CodeReviewAgent agent) {
this.codeReviewAgent = agent;
}
@Override
public void execute(RequestContext context, AgentEmitter agentEmitter) throws JSONRPCError {
if (context.getTask() == null) {
agentEmitter.submit();
}
agentEmitter.startWork();
String code = extractTextFromMessage(context.getMessage());
String review = codeReviewAgent.review(code);
TextPart responsePart = new TextPart(review);
agentEmitter.addArtifact(List.of(responsePart));
agentEmitter.complete();
}
@Override
public void cancel(RequestContext context, AgentEmitter agentEmitter) throws JSONRPCError {
Task task = context.getTask();
if (task.getStatus().state() == TaskState.COMPLETED
|| task.getStatus().state() == TaskState.CANCELED) {
throw new TaskNotCancelableError();
}
agentEmitter.cancel();
}
private String extractTextFromMessage(Message message) {
StringBuilder sb = new StringBuilder();
for (Part> part : message.parts()) {
if (part instanceof TextPart textPart) {
sb.append(textPart.text());
}
}
return sb.toString();
}
}
}
```
## Native implementation (without an SDK)
[Section titled “Native implementation (without an SDK)”](#native-implementation-without-an-sdk)
If you need full control or work in a language without an official SDK, implement the A2A protocol directly over JSON-RPC 2.0.
### What you must implement
[Section titled “What you must implement”](#what-you-must-implement)
1. `GET /.well-known/agent.json` — serve your Agent Card.
2. `POST /` — handle JSON-RPC requests:
* `message/send` — accept a message and return a task.
* `tasks/get` — return task status.
* `message/stream` — SSE stream of updates (for streaming agents).
### Python — native with Flask
[Section titled “Python — native with Flask”](#python--native-with-flask)
agent.py
```python
import json
import uuid
from flask import Flask, request, jsonify, Response
app = Flask(__name__)
tasks = {}
AGENT_CARD = {
"name": "Expense Report Agent",
"description": "Processes and categorizes expense reports",
"url": "http://localhost:5000",
"version": "1.0.0",
"capabilities": {"streaming": False, "pushNotifications": False},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["application/json"],
"skills": [
{
"id": "categorize-expenses",
"name": "Categorize Expenses",
"description": "Categorizes line items into budget categories",
"tags": ["finance", "expenses"],
"examples": ["Categorize these 5 expense line items"],
}
],
}
@app.route("/.well-known/agent.json", methods=["GET"])
def agent_card():
return jsonify(AGENT_CARD)
@app.route("/", methods=["POST"])
def handle_jsonrpc():
body = request.get_json()
method = body.get("method")
params = body.get("params", {})
request_id = body.get("id")
if method == "message/send":
return handle_send_message(params, request_id)
elif method == "tasks/get":
return handle_get_task(params, request_id)
else:
return jsonify({
"jsonrpc": "2.0",
"id": request_id,
"error": {"code": -32601, "message": f"Method not found: {method}"},
})
def handle_send_message(params, request_id):
message = params.get("message", {})
context_id = params.get("metadata", {}).get("contextId", str(uuid.uuid4()))
parts = message.get("parts", [])
user_text = " ".join(p["text"] for p in parts if p.get("kind") == "text")
result = categorize_expenses(user_text)
task_id = str(uuid.uuid4())
task = {
"kind": "task",
"id": task_id,
"contextId": context_id,
"status": {
"state": "completed",
"message": {
"role": "agent",
"parts": [{"kind": "data", "data": result}],
},
},
"artifacts": [
{
"artifactId": str(uuid.uuid4()),
"parts": [{"kind": "data", "data": result}],
"lastChunk": True,
}
],
}
tasks[task_id] = task
return jsonify({"jsonrpc": "2.0", "id": request_id, "result": task})
def handle_get_task(params, request_id):
task_id = params.get("id")
task = tasks.get(task_id)
if not task:
return jsonify({
"jsonrpc": "2.0",
"id": request_id,
"error": {"code": -32602, "message": "Task not found"},
})
return jsonify({"jsonrpc": "2.0", "id": request_id, "result": task})
def categorize_expenses(text: str) -> dict:
return {
"categories": [
{"item": "Client dinner", "category": "Meals & Entertainment", "amount": 127.50},
{"item": "Uber to airport", "category": "Transportation", "amount": 45.00},
{"item": "Conference ticket", "category": "Professional Development", "amount": 599.00},
],
"total": 771.50,
}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
```
### TypeScript — native with Express and SSE streaming
[Section titled “TypeScript — native with Express and SSE streaming”](#typescript--native-with-express-and-sse-streaming)
Demonstrates SSE streaming via the `message/stream` method to push updates asynchronously.
agent.ts
```typescript
import express from 'express';
import { v4 as uuidv4 } from 'uuid';
const app = express();
app.use(express.json());
const tasks: Record = {};
const AGENT_CARD = {
name: 'Invoice Processing Agent',
description: 'Extracts structured data from invoices and receipts',
url: 'http://localhost:4000',
version: '1.0.0',
capabilities: { streaming: true, pushNotifications: false },
defaultInputModes: ['text/plain', 'application/pdf'],
defaultOutputModes: ['application/json'],
skills: [
{
id: 'extract-invoice',
name: 'Extract Invoice Data',
description: 'Parses invoices and returns structured line items',
tags: ['finance', 'ocr', 'extraction'],
examples: ['Extract all line items from this invoice'],
},
],
};
app.get('/.well-known/agent.json', (req, res) => {
res.json(AGENT_CARD);
});
app.post('/', (req, res) => {
const { method, params, id: requestId } = req.body;
switch (method) {
case 'message/send':
return handleSendMessage(params, requestId, res);
case 'message/stream':
return handleStreamMessage(params, requestId, res);
case 'tasks/get':
return handleGetTask(params, requestId, res);
default:
return res.json({
jsonrpc: '2.0',
id: requestId,
error: { code: -32601, message: `Method not found: ${method}` },
});
}
});
function handleSendMessage(params: any, requestId: string, res: express.Response) {
const userText = params.message.parts
.filter((p: any) => p.kind === 'text')
.map((p: any) => p.text)
.join(' ');
const taskId = uuidv4();
const result = processInvoice(userText);
const task = {
kind: 'task',
id: taskId,
contextId: params.metadata?.contextId || uuidv4(),
status: { state: 'completed' },
artifacts: [{ artifactId: uuidv4(), parts: [{ kind: 'data', data: result }], lastChunk: true }],
};
tasks[taskId] = task;
res.json({ jsonrpc: '2.0', id: requestId, result: task });
}
function handleStreamMessage(params: any, requestId: string, res: express.Response) {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const taskId = uuidv4();
const contextId = params.metadata?.contextId || uuidv4();
// Emit working status
res.write(`data: ${JSON.stringify({
jsonrpc: '2.0',
method: 'tasks/statusUpdate',
params: { taskId, contextId, status: { state: 'working' } },
})}\n\n`);
setTimeout(() => {
const result = processInvoice(
params.message.parts.filter((p: any) => p.kind === 'text').map((p: any) => p.text).join(' ')
);
// Emit artifact
res.write(`data: ${JSON.stringify({
jsonrpc: '2.0',
method: 'tasks/artifactUpdate',
params: {
taskId,
contextId,
artifact: { artifactId: uuidv4(), parts: [{ kind: 'data', data: result }], lastChunk: true },
},
})}\n\n`);
// Emit completed status
res.write(`data: ${JSON.stringify({
jsonrpc: '2.0',
method: 'tasks/statusUpdate',
params: { taskId, contextId, status: { state: 'completed' } },
})}\n\n`);
res.end();
}, 1000);
}
function handleGetTask(params: any, requestId: string, res: express.Response) {
const task = tasks[params.id];
if (!task) {
return res.json({
jsonrpc: '2.0',
id: requestId,
error: { code: -32602, message: 'Task not found' },
});
}
res.json({ jsonrpc: '2.0', id: requestId, result: task });
}
function processInvoice(text: string) {
return {
vendor: 'Acme Corp',
invoiceNumber: 'INV-2026-0847',
lineItems: [
{ description: 'Consulting services', quantity: 40, unitPrice: 150, total: 6000 },
{ description: 'Travel expenses', quantity: 1, unitPrice: 1200, total: 1200 },
],
subtotal: 7200,
tax: 648,
total: 7848,
};
}
app.listen(4000, () => {
console.log('Invoice Processing Agent running on port 4000');
});
```
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
* **Streaming responses aren’t received by the client** — confirm `capabilities.streaming` is `true` in your Agent Card and that you emit SSE events on `message/stream`.
* **Tasks stay in `working` indefinitely** — ensure every path calls `complete()`, `fail()`, or `cancel()`.
* **Cancellation isn’t honored** — implement `cancel()` and guard against canceling already-`completed`/`canceled` tasks.
## Further reading
[Section titled “Further reading”](#further-reading)
* [A2A Protocol Specification](https://a2a-protocol.org/latest/specification/)
* Official SDKs: [Python](https://github.com/a2aproject/a2a-python) · [TypeScript](https://github.com/a2aproject/a2a-js) · [Java](https://github.com/a2aproject/a2a-java)
* [LangGraph documentation](https://langchain-ai.github.io/langgraph/) · [`langchain-ai/langgraph`](https://github.com/langchain-ai/langgraph)
* [Technology Compatibility Kit (TCK)](https://github.com/a2aproject/a2a-tck)
* Next: [Connect your agent to Foldspace →](/guides/a2a-connect/)
# Connect an A2A agent
> Register your A2A server agent in Foldspace Agent Studio — connection details, authentication, user-context forwarding, and timeouts.
Once your [A2A server agent](/guides/a2a-server-agents/) is deployed and reachable, connect it to a Copilot in **Agent Studio → A2A Agents**. The Copilot then discovers your agent’s skills and delegates matching tasks to it automatically.
## Before you begin
[Section titled “Before you begin”](#before-you-begin)
* Your remote A2A server agent must run over **HTTPS** and be reachable.
* It must serve an Agent Card at `/.well-known/agent.json` (or a custom URL).
* You need the agent’s **base URL** and **authentication credentials**.
* A Copilot can connect up to **20** A2A agents.
## The agent list
[Section titled “The agent list”](#the-agent-list)
**Agent Studio → A2A Agents** lists every agent connected to this Copilot:
| Column | What it shows |
| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------ |
| **Name** | The display name you gave the agent. This is also how it appears in the **Actions** filter in [Conversations](/user-guides/conversations/). |
| **Base URL** | The root URL of the remote agent. |
| **Auth** | The auth type in use — **None**, **Bearer Token**, or **API Key**. |
| **Agent Card** | The name from the fetched Agent Card and its skill count. A green check means the card was fetched and validated. |
| **Status** | Toggle an agent on or off. A disabled agent stays configured, but the Copilot won’t invoke it. |
Use **Add Agent** to connect a new one, or the **⋯** menu on a row for per-agent options.
## Connection details
[Section titled “Connection details”](#connection-details)
**Path:** Agent Studio → A2A Agents → **Add Agent**
| Field | Required | Description |
| :------------------- | :------- | :---------------------------------------------------------------------------------------------- |
| **Display Name** | Yes | A friendly name for this agent (e.g. “Status Monitor Agent”). Shown in the Copilot’s tool list. |
| **Base URL** | Yes | The root URL where the remote A2A agent is hosted (e.g. `https://my-agent.example.com`). |
| **Agent Card URL** | No | Override the default card location. Leave blank to use `/.well-known/agent.json`. |
| **Protocol Version** | Yes | Currently **A2A v0.3** (auto-selected). |
## Authentication
[Section titled “Authentication”](#authentication)
| Auth type | When to use | Fields |
| :--------------- | :--------------------------------------------------------- | :---------- |
| **None** | Agent is open or network-restricted (no auth headers sent) | — |
| **Bearer Token** | Agent expects `Authorization: Bearer ` | Token value |
| **API Key** | Agent expects `X-API-Key: ` | Key value |
Note
Secrets are encrypted at rest. Once saved, they display masked (`••••••••`). Click **Replace** to rotate a credential.
After saving, click **Fetch Agent Card** to validate the connection and pull in your agent’s skills, then choose which skills to **expose** to the Copilot.
## Advanced settings (optional)
[Section titled “Advanced settings (optional)”](#advanced-settings-optional)
### Forward user context
[Section titled “Forward user context”](#forward-user-context)
Toggle on to pass the current user’s identity to the remote agent via the `X-Foldspace-User-Id` header. You can also include a structured **user-context data part**:
| Source field | Default key | Description |
| :------------------ | :---------------- | :------------------------------------- |
| **User Email** | `user_email` | The authenticated user’s email address |
| **User ID** | `user_id` | The user’s unique identifier |
| **Subscription ID** | `subscription_id` | The customer’s subscription identifier |
### Additional headers
[Section titled “Additional headers”](#additional-headers)
Add static custom headers (for API versioning, routing, etc.) sent on every request to your agent.
### Connection timeouts
[Section titled “Connection timeouts”](#connection-timeouts)
| Setting | Default | Range | Description |
| :------------------ | :------ | :----- | :------------------------------------------ |
| **Connect Timeout** | 10s | 1–300s | Max time to establish a connection |
| **Request Timeout** | 120s | 1–600s | Max time to wait for a single HTTP response |
| **Task Timeout** | 300s | 1–900s | Max total time for an A2A task to complete |
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
Start in Agent Studio → **Conversations** and open the **Actions** filter — your A2A agent is listed there by its display name. Select it and click **Apply** to narrow the list to conversations that delegated to it. Open one, click **View Analysis** on the agent message, and check the **Actions** tab: each A2A call shows a **Remote Agent** badge, its status, and — when expanded — the **Arguments** sent, the **Response** returned, and the execution time. That’s usually enough to tell a timeout apart from an auth failure or a bad payload.
| Problem | Solution |
| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------- |
| **”Failed to fetch agent card”** | Verify the Base URL is reachable, the Agent Card URL serves valid JSON, and your auth credentials are correct. |
| **Agent shows but the Copilot doesn’t invoke it** | Check that the agent is **Enabled** and has at least one **Exposed** skill. |
| **Authentication errors at runtime** | Confirm the token or API key hasn’t expired or been revoked. |
| **Agent times out during execution** | Increase the **Task Timeout** in Advanced settings (default 300s). |
## Related
[Section titled “Related”](#related)
* [A2A server agents](/guides/a2a-server-agents/) — concepts and the Agent Card.
* [A2A quickstart](/guides/a2a-quickstart/) — build your first agent.
* [A2A advanced patterns](/guides/a2a-advanced/) — LangGraph, TypeScript, Java, native.
* [A2A agents (Agent Studio)](/user-guides/a2a-agents/) — the product overview.
# A2A quickstart
> Build, run, and test your first A2A server agent in Python using the official A2A SDK, then connect it to the Foldspace Copilot.
This quickstart builds a Travel Booking A2A server agent in Python with the official A2A SDK, then runs and tests it locally. For the concepts behind A2A, start with [A2A server agents](/guides/a2a-server-agents/).
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* Python 3.10+
* Familiarity with async Python
## Step 1: Install the SDK
[Section titled “Step 1: Install the SDK”](#step-1-install-the-sdk)
Install the official Python A2A SDK plus `uvicorn` to serve the app.
```bash
pip install a2a-sdk uvicorn
```
## Step 2: Implement your agent
[Section titled “Step 2: Implement your agent”](#step-2-implement-your-agent)
The `AgentExecutor` holds your business logic, the `AgentCard` describes your capabilities, and `A2AFastAPIApplication` provides the HTTP layer.
agent.py
```python
import uvicorn
from a2a.server.apps.jsonrpc import A2AFastAPIApplication
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
from a2a.types import (
AgentCard,
AgentCapabilities,
AgentSkill,
Part,
TextPart,
)
class TravelBookingExecutor(AgentExecutor):
"""Handles flight and hotel search requests."""
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.start_work()
user_input = context.get_user_input()
result = await self._search_travel_options(user_input)
await updater.add_artifact(parts=[Part(root=TextPart(text=result))])
await updater.complete()
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
await updater.cancel()
async def _search_travel_options(self, query: str) -> str:
# Replace with your actual logic — call an LLM, query APIs, etc.
return f"Found 3 flight options and 5 hotels matching: {query}"
# --- Agent Card: describes what your agent can do ---
agent_card = AgentCard(
name="Travel Booking Agent",
description="Searches flights, hotels, and packages based on travel preferences",
url="http://localhost:8080/",
version="1.0.0",
capabilities=AgentCapabilities(streaming=True),
default_input_modes=["text/plain"],
default_output_modes=["text/plain", "application/json"],
skills=[
AgentSkill(
id="search-flights",
name="Search Flights",
description="Find available flights between destinations",
tags=["travel", "flights", "booking"],
examples=["Find flights from NYC to London next Friday"],
),
AgentSkill(
id="search-hotels",
name="Search Hotels",
description="Find hotels at a destination with filters",
tags=["travel", "hotels", "accommodation"],
examples=["Find 4-star hotels in Paris for 3 nights"],
),
],
)
# --- Wire up and start the server ---
request_handler = DefaultRequestHandler(
agent_executor=TravelBookingExecutor(),
task_store=InMemoryTaskStore(),
)
app = A2AFastAPIApplication(
agent_card=agent_card,
http_handler=request_handler,
).build()
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8080)
```
## Step 3: Run it
[Section titled “Step 3: Run it”](#step-3-run-it)
```bash
python agent.py
```
## Step 4: Test it
[Section titled “Step 4: Test it”](#step-4-test-it)
Fetch the published Agent Card:
```bash
curl http://localhost:8080/.well-known/agent.json | jq .
```
Send a message via JSON-RPC `message/send`:
```bash
curl -X POST http://localhost:8080/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "test-001",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "Find flights from SF to Tokyo next week"}],
"messageId": "msg-001"
}
}
}'
```
Validate protocol compliance with the A2A Inspector:
```bash
npx @a2a-js/inspector http://localhost:8080
```
## How it works
[Section titled “How it works”](#how-it-works)
The A2A server comprises three key components:
1. **AgentExecutor** — implements business logic, receiving a `RequestContext` (the user’s message) and publishing results to an `EventQueue`.
2. **DefaultRequestHandler** — handles protocol details, routing JSON-RPC calls to your executor and managing task lifecycle and storage.
3. **A2AFastAPIApplication** — the HTTP layer that builds a FastAPI/Starlette app with endpoints like `/.well-known/agent.json` and the JSON-RPC POST.
Request flow: Client → `A2AFastAPIApplication` (HTTP) → `DefaultRequestHandler` (task lifecycle) → your executor → `TaskUpdater` publishes status/artifacts to the `EventQueue` → events return as an SSE stream (streaming) or a single JSON-RPC response (non-streaming).
Note
The text you send via `add_artifact(...)` is what the Foldspace Copilot shows as the **answer**. Status updates (`update_status(state="working", ...)`) are treated as **progress** only.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
| Problem | Fix |
| :-------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- |
| **Agent Card not found at `/.well-known/agent.json`** | Verify the correct port and an error-free server startup. |
| **Tasks remain in `working` state** | Ensure your executor calls `complete()`, `fail()`, or `cancel()` in all code paths, including errors. |
| **Authentication fails when Foldspace invokes the agent** | Confirm your Agent Card’s `authentication.schemes` matches what the server validates. |
## Next steps
[Section titled “Next steps”](#next-steps)
* **[Advanced patterns →](/guides/a2a-advanced/)** — LangGraph, TypeScript, Java, native (no-SDK), and streaming.
* **[Connect to Foldspace →](/guides/a2a-connect/)** — register your agent in Agent Studio.
# A2A server agents
> Build an Agent-to-Agent (A2A) server agent that the Foldspace Copilot can discover, delegate tasks to, and stream results from — with Python, TypeScript, Java, and LangGraph examples.
Agent-to-Agent (A2A) lets your Foldspace Copilot delegate work to an external AI agent — your **A2A server agent** — over a standard protocol, then stream the result back into the conversation.
This is the developer guide for **building** an A2A server agent. For the product / no-code side of connecting one in Agent Studio, see the [A2A agents user guide](/user-guides/a2a-agents/).
## What is an A2A server agent?
[Section titled “What is an A2A server agent?”](#what-is-an-a2a-server-agent)
An A2A server agent (also called a “remote agent”) is an AI-powered service that exposes its capabilities over HTTP using the [A2A protocol](https://a2a-protocol.org). Any A2A-compatible client — including orchestrators, platforms like Foldspace, or other agents — can discover your agent via its **Agent Card**, send it tasks, and receive results through a standardized interface.
The protocol is built on **JSON-RPC 2.0 over HTTP(S)**, supports **Server-Sent Events (SSE)** for streaming, and uses **Agent Cards** for capability discovery.
## How Foldspace uses A2A
[Section titled “How Foldspace uses A2A”](#how-foldspace-uses-a2a)
In Foldspace, the **Copilot agent acts as the A2A client**. When a user interacts with a Copilot, it discovers your server agent via its Agent Card, delegates tasks to it, and presents results back to the user. Your job is to build the server side.
```text
User → Foldspace Copilot (A2A client) → Your server agent (A2A server)
← Task results / streaming updates
```
Your A2A server agent is invoked automatically when the Copilot determines your agent’s skills match the user’s intent. You only need to build and register the server side — Foldspace handles all client-side orchestration.
### What Foldspace supports
[Section titled “What Foldspace supports”](#what-foldspace-supports)
| Capability | How it works |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Streaming** | Real-time SSE updates. When your agent emits status or artifact events, users see progress live in the conversation. |
| **Tool progress** | When your agent invokes tools during execution, Foldspace surfaces progress indicators to users automatically. |
| **Authentication** | Configure your Agent Card’s auth schemes (Bearer, API key) and Foldspace manages credential passing. |
| **User input (multi-turn)** | Set task state to `input-required` and the Copilot prompts the user, then relays their response back to your agent. |
## Key concepts
[Section titled “Key concepts”](#key-concepts)
| Concept | Description |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Agent Card** | A JSON manifest describing your agent’s name, skills, endpoints, authentication, and capabilities. Hosted at `/.well-known/agent.json`. |
| **Task** | The unit of work. Has a lifecycle: `submitted` → `working` → `completed` / `failed` / `canceled`. |
| **Message** | Carries multimodal content (text, files, structured data) between client and server. |
| **Artifact** | The output/result of a completed task, streamed incrementally. |
| **Streaming (SSE)** | Real-time status updates and partial results over Server-Sent Events. |
Note
The text you send via `add_artifact(...)` is what the Foldspace Copilot shows as the **answer**. Status updates (`update_status(state="working", ...)`) are treated as **progress** only.
## Agent Card reference
[Section titled “Agent Card reference”](#agent-card-reference)
The Agent Card is the entry point for all A2A interactions. It tells clients what your agent can do and how to reach it, and is served at `/.well-known/agent.json`.
/.well-known/agent.json
```json
{
"name": "Your Agent Name",
"description": "What your agent does in one sentence",
"url": "https://your-agent.example.com/a2a",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"default_input_modes": ["text/plain"],
"default_output_modes": ["text/plain", "application/json"],
"skills": [
{
"id": "skill-identifier",
"name": "Human-Readable Skill Name",
"description": "What this skill does",
"tags": ["relevant", "tags"],
"examples": ["Example input that triggers this skill"]
}
],
"authentication": {
"schemes": ["bearer"]
}
}
```
**Required fields:** `name`, `url`, `version`, `capabilities`, `skills`
## Implementation approaches
[Section titled “Implementation approaches”](#implementation-approaches)
There are three ways to build an A2A server agent:
| Approach | Best for | Languages |
| ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------ |
| **Official SDK** | Most developers — handles protocol compliance automatically | Python, TypeScript, Java |
| **[LangGraph](https://www.langchain.com/langgraph) + SDK** | Teams already using [LangGraph](https://langchain-ai.github.io/langgraph/) for agent orchestration | Python |
| **Native (no SDK)** | Full control, unsupported languages, or minimal dependencies | Any language with HTTP support |
Already orchestrating agents with [LangGraph](https://github.com/langchain-ai/langgraph)? You can expose an existing LangGraph graph as an A2A server agent with a thin executor bridge — see [Advanced patterns](/guides/a2a-advanced/#langgraph-integration).
## Next steps
[Section titled “Next steps”](#next-steps)
* **[Quickstart →](/guides/a2a-quickstart/)** — build and test your first A2A server agent in Python.
* **[Advanced patterns →](/guides/a2a-advanced/)** — LangGraph, TypeScript, Java, native (no-SDK), and streaming.
* **[Connect to Foldspace →](/guides/a2a-connect/)** — register your agent in Agent Studio.
# How Our Agent Thinks, Acts, and Prevents Loops
> Understand how the Foldspace agent plans, selects tools, executes actions, manages iterations, and prevents infinite loops for safe, reliable automation.
## Agentic Capabilities: Planning, Tool Selection, and Iterative Execution
[Section titled “Agentic Capabilities: Planning, Tool Selection, and Iterative Execution”](#agentic-capabilities-planning-tool-selection-and-iterative-execution)
Foldspace’s embedded agent is designed to make SaaS applications smart, personalized, and easy to use by transforming natural language input into actions, workflows, and interactive UI. This article explains how Foldspace supports agentic capabilities across the lifecycle of intent → planning → execution → iteration.
## 1. Intent Determination and Tool Selection
[Section titled “1. Intent Determination and Tool Selection”](#1-intent-determination-and-tool-selection)
The agent understands their intent, triggers the right workflows, and can also display interactive UI components (like forms, cards, or dashboards) within the conversation.
* Users can simply describe what they want in text or voice.
* The agent uses natural language understanding to parse the request, determine intent, and map it to the correct workflow.
* Actions and Schemas are defined in Agent Studio, allowing the agent to dynamically select and execute them.
## 2. Action Planning and Execution
[Section titled “2. Action Planning and Execution”](#2-action-planning-and-execution)
Foldspace supports AI Actions, which are custom functions that the agent can trigger.
Actions can be:
* **Execute-only** (e.g., trigger a workflow, fetch data)
* **Interactive** (execute + render results via Chatterblocks, such as forms, cards, or dashboards)
The agent can:
* Collect required parameters from the user.
* Execute the action.
* Render results or UI components inline in the conversation.
## 3. Plan Generation
[Section titled “3. Plan Generation”](#3-plan-generation)
By design, the agent can detect dependencies between different agentic experiences and tie them to the user’s intent.
This allows the same high-level intent to drive different execution paths, depending on the user’s context.
You can design an action that:
* Calls other agentic functions in sequence, or
* Uses a deterministic execution block to orchestrate a clear, ordered flow.
This gives you flexibility: workflows can be both adaptive to user needs and deterministic when required.
## 4. Iterative Execution and Looping
[Section titled “4. Iterative Execution and Looping”](#4-iterative-execution-and-looping)
* The agent can handle stepwise workflows where it waits for user input before proceeding.
* It supports chaining actions (e.g., query → refine → confirm → execute).
* Iteration is typically user-driven: the user edits data, the agent processes it, and the user confirms or cancels.
## 5. Exit Criteria and Loop Termination
[Section titled “5. Exit Criteria and Loop Termination”](#5-exit-criteria-and-loop-termination)
Actions can be configured to complete when conditions are met, such as:
* User saves or cancels changes.
* Required inputs are collected.
* The agent awaits user input before proceeding to the next step.
Safety mechanisms are in place to prevent infinite loops by detecting repetitive cycles and enforcing termination safeguards. These mechanisms ensure that the agent does not continue indefinitely without resolution.
## 6. Summary Table
[Section titled “6. Summary Table”](#6-summary-table)
| Capability Aspect | Support Level | Notes |
| :------------------------ | :-------------------- | :--------------------------------------------------------------------------------------------------------- |
| Intent Determination | Yes | Natural language understanding and workflow triggering |
| Tool/Action Selection | Yes | Actions and schemas defined in Agent Studio |
| Plan Generation | Yes | Detects dependencies across agentic experiences; supports chaining, function calls, or deterministic flows |
| Iterative Execution | Partial (User-driven) | Stepwise, with user-in-the-loop; not fully autonomous |
| Exit Criteria/Termination | Yes | User-driven completion and built-in safeguards against infinite loops |
## 7. Conclusion
[Section titled “7. Conclusion”](#7-conclusion)
Foldspace provides strong support for:
* Intent determination
* Tool/action selection
* Dependency-aware planning and deterministic flows
* Stepwise execution with interactive UI
In addition, the platform includes safety mechanisms to prevent infinite loops, ensuring reliable agent behavior.
For fully autonomous, multi-step execution with internal planning, you may extend logic inside your Action Handlers or integrate Foldspace with external orchestration systems for advanced loop control.
# The agentic loop
> The Foldspace SDK runs the same evaluate-act-repeat agent loop that powers the Claude Agent SDK, chaining multiple actions until the request is done.
A user prompt doesn’t get answered in a single pass. Foldspace runs an *agentic loop*: it evaluates the prompt against real-time context, makes one or more action calls, feeds each action result back into its next evaluation, and repeats until there’s nothing left to call. This is the same loop that powers the [Claude Agent SDK](https://code.claude.com/docs/en/agent-sdk/agent-loop) — the Foldspace SDK runs it inside your product, grounded in your users, your knowledge, and your actions.
## Core concept
[Section titled “Core concept”](#core-concept)
A single prompt turns into a cycle rather than a one-shot reply:
1. **User prompt** — what the user asks, in natural language.
2. **Foldspace real-time context** — the agent assembles [usage, knowledge, and action context](/guides/context/) for this turn: who the user is, the page they’re on, and what it can do.
3. **Foldspace evaluates** — the agent reasons over the current state and decides the next step: make action calls, or reply.
4. **Action calls** — **one or more actions** run, and each **action result** feeds back into the next evaluation.
5. **Final reply** — when no actions are left to call, the agent answers.
The loop is what separates an agent from a chatbot. A chatbot maps a message to a response. An agent evaluates, acts on your product, reads each result, and keeps going until the prompt is actually resolved.
## It chains more than one action
[Section titled “It chains more than one action”](#it-chains-more-than-one-action)
A request rarely resolves in a single action. Within one evaluation the agent can request **several actions at once**, and across the loop it **chains many actions in sequence** — each one informed by the result of the last.
* **Multiple actions per turn.** When the agent needs several things at once, it requests them together. Independent, read-only actions can run in parallel; actions that change state run in order.
* **Many actions across turns.** A high-level request — “refund this order and email the customer” — becomes a chain: look up the order, issue the refund, then send the confirmation, with the agent re-evaluating after each result.
* **Adaptive, not scripted.** The agent decides the next action from what came back, so the same request can take a different path depending on the user, the page, and the data it sees.
This is exactly how the Claude Agent SDK behaves: a quick request might call one action, while a complex one chains many actions across turns before the agent is done.
## The same loop as the Claude Agent SDK
[Section titled “The same loop as the Claude Agent SDK”](#the-same-loop-as-the-claude-agent-sdk)
The Claude Agent SDK runs a cycle — evaluate, call tools, feed results back, repeat — that continues until the model responds with no tool calls. The Foldspace SDK runs that identical loop, with each stage bound to the parts of your product the agent is embedded in.
| Claude Agent SDK stage | What the Foldspace SDK does |
| :----------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Receive prompt** | Takes in the request with its [assembled context](/guides/context/) — who the user is, the page they’re on, the facts it can draw on, and the actions available. |
| **Evaluate and respond** | Reasons over the current state and either calls **one or more [actions](/guides/build-actions/)** or produces a reply. |
| **Execute tools** | Runs each action’s [`execute()` handler](/guides/executing-actions/), fetching data or [rendering an interactive component](/guides/in-chat-ui/) in the chat, and feeds the result back. |
| **Repeat** | Cycles through evaluation and execution, chaining actions across turns until no further action is needed. |
| **Return result** | Delivers the final reply once the agent responds with no more actions to run. |
Because the loop shape is the same, patterns you know from the Claude Agent SDK carry over: give the agent good context, expose the right actions, and let it iterate.
## How the loop runs
[Section titled “How the loop runs”](#how-the-loop-runs)
### Evaluate
[Section titled “Evaluate”](#evaluate)
The agent doesn’t load everything up front. For each pass it assembles *only* the context relevant to the request — usage context (identity, subscription, live page), knowledge context (the facts it answers from), and action context (what it can do) — and reasons over it to decide the next step. See [Context](/guides/context/) for how each source is wired up.
### Act
[Section titled “Act”](#act)
When the agent decides to act, it extracts the parameters each action needs from the user’s intent and calls it — often more than one at a time. An action can run pure logic and return data for the agent to reason over, or [render UI in the chat](/guides/in-chat-ui/) — a form, card, or dashboard — when the request calls for interaction.
### Repeat
[Section titled “Repeat”](#repeat)
Each action’s result feeds back into the next evaluation. The agent chains as many actions as the request needs — query, refine, confirm, execute — re-evaluating after each one. Where a step needs the user, an action can [pause for input](/guides/executing-actions/) with `awaitUserInput` before the loop continues.
### Finish
[Section titled “Finish”](#finish)
The loop ends when the agent responds with no further actions to run, or when an exit condition is met — the user saves or cancels, required inputs are collected, or a safeguard halts a repetitive cycle. Built-in loop-termination safeguards keep the agent from running indefinitely.
## Why it matters
[Section titled “Why it matters”](#why-it-matters)
The agentic loop is why the same intent can resolve different ways depending on who’s asking and what they’re doing. Instead of hard-coding one path per request, you give the agent context and actions, and the loop composes the steps — chaining as many actions as it takes — at runtime: adaptive when the situation calls for it, and deterministic where you need a fixed order.
## Related
[Section titled “Related”](#related)
* [Context](/guides/context/) — the three kinds of context the agent evaluates each pass.
* [Execute actions](/guides/executing-actions/) — the handler behind each action the agent runs.
* [How our agent thinks, acts, and prevents loops](/guides/agentic-capabilities/) — planning, tool selection, and termination in more detail.
# Build actions
> Actions are the capabilities the agent runs in your product — defined in Agent Studio, implemented in your code.
Actions are what the agent can *do*: call your APIs, submit forms, navigate your UI, or render a component. Each action has two halves — you **define** it in Agent Studio (name, description, input schema) and **implement** it in your code (a handler registered under the action’s key). The agent decides *when* to call an action from the user’s request; your handler runs it and returns data or renders UI.
[What are Actions ](/guides/text-driven-actions/)The model: text-driven capabilities the agent routes requests to.
[Connect actions ](/guides/connecting-actions/)Register a handler in your code under the action's key.
[Execute actions ](/guides/executing-actions/)What runs at call time, and how to return data or render UI.
## Where to start
[Section titled “Where to start”](#where-to-start)
Read [What are Actions](/guides/text-driven-actions/) for the model, then [Connect actions](/guides/connecting-actions/) to wire a handler, then [Execute actions](/guides/executing-actions/) for the runtime.
## Related
[Section titled “Related”](#related)
* [Authoring actions](/user-guides/authoring-actions/): define actions in the Agent Studio dashboard.
* [Agentic UI](/guides/recipes/): render UI components from an action.
# Connect actions
> Register a handler in your code for an action defined in Agent Studio, so the agent can run real logic in your product.
An action has two halves. In **Agent Studio**, you define what it is — a name, a description, and the input schema the agent fills in from the user’s request. In **your code**, you implement what it does. Connecting an action wires those halves together: you register a handler under the action’s key, and the agent calls it whenever it routes a request to that action.
```
flowchart LR
subgraph STUDIO["Agent Studio"]
D[Define action name & description]
S[Set input schema & response mock]
P[Publish version]
end
subgraph CODE["Your application"]
C[Take action key & schema]
I[Register handler under that key]
W[Run logic, return data or render UI]
end
D --> S --> P
P -- "key, schema, expected output" --> C
C --> I --> W
```
## What you need from Studio
[Section titled “What you need from Studio”](#what-you-need-from-studio)
To connect an action, take three things from its definition in Agent Studio:
| Item | What it is |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **Action Key** | The action’s unique identifier (e.g. `create_task`, `invite_user`). You register your handler under this exact key. |
| **Input Schema** | The fields the agent extracts and passes to your handler as `params` (e.g. `email`, `name`, `organizationRoleType`). |
| **Expected Output** | What the action should do, plus any data the agent should use in its reply — for example, a link to deep-link the user to a page. |
## Register a handler
[Section titled “Register a handler”](#register-a-handler)
Register handlers on the agent with `addActionHandlers`. Each key matches an Action Key from Studio, and the agent calls that handler with the schema fields as `params`:
```javascript
foldspace("when", "ready", () => {
foldspace.agent({ /* …common setup… */ })
.addActionHandlers({
create_task: {
execute: async (params) => {
// `params` matches the action's input schema, e.g. { title, body }
// …do the work, then return data for the agent…
},
},
});
});
```
What goes inside the handler depends on what the action should return:
* **[Execute actions](/guides/executing-actions/)** — run logic and return data the agent relays in its next reply.
* **[UI components in chat](/guides/in-chat-ui/)** — render an interactive component in the chat and send the user’s input back to the agent.
Note
See the **Developer Guide → [Coding Actions](https://foldspace.readme.io/docs/ai-actions-2)** for full implementation details.
Tip
Think of Studio as the **blueprint** for an action, and your application code as the **engine** that runs it.
## Next steps
[Section titled “Next steps”](#next-steps)
* [Execute actions](/guides/executing-actions/): what runs when the agent calls your handler, and how to return data.
* [UI components in chat](/guides/in-chat-ui/): render an interactive component from an action.
# Context
> The agent assembles three kinds of context — usage, knowledge, and action — in real time to answer with low latency, high quality, and accuracy.
Every time the agent responds, it assembles three kinds of context in real time and reasons over them together. Getting the *right* context in front of the model, and only the right context, is what makes the agentic interface fast, accurate, and genuinely useful.
## The three contexts
[Section titled “The three contexts”](#the-three-contexts)
| Context | What it is | Where it comes from |
| :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Usage context** *(real-time)* | Who the user is and what they’re doing right now: identity, subscription, and custom attributes, plus the live page they’re looking at. | [`identify()`](/start/user-context/) for the user and subscription; [Shared State (Tandem)](/guides/shared-state/) and [Share current tab](/customize/share-current-tab/) for the page. |
| **Knowledge context** | The facts the agent can draw on to answer. | [Knowledge Base](/user-guides/knowledge-base/) |
| **Action context** | What the agent can do: the actions available to run. | [Actions](/guides/build-actions/) |
## Assembled in real time
[Section titled “Assembled in real time”](#assembled-in-real-time)
The agent doesn’t load everything up front. For each turn it selects only the context relevant to the request and assembles it on the fly. That real-time assembly is what maximizes:
* **Latency** — only the context that matters is sent to the model, so responses come back fast.
* **Quality** — replies reflect who the user is and what they’re doing on the page right now.
* **Accuracy** — answers are grounded in your knowledge and act through your actions, instead of guessing.
## Wire up each context
[Section titled “Wire up each context”](#wire-up-each-context)
[Usage context ](/start/user-context/)Identify users and subscriptions with attributes, and share live page state (Shared State / Share current tab).
[Knowledge context ](/user-guides/knowledge-base/)Give the agent the content it answers from.
[Action context ](/guides/build-actions/)Define the actions the agent can run in your product.
# Embedded mode
> Mount the Foldspace agent as a web component inside your application by initializing it into a DOM container.
Embedded mode renders the agent as a web component directly inside your application, anchored to a DOM element you control. Use it when you want the chat to live inline in your layout rather than as a floating overlay.
Prerequisite
This page assumes you’ve already installed the [SDK](/start/install/).
## Initialize the agent
[Section titled “Initialize the agent”](#initialize-the-agent)
The snippet below waits for the SDK to be ready, then mounts the agent into the container element you pass.
```javascript
// Replace with the API name from Agent Studio → Setup
const AGENT_API_NAME = "Foo";
const agentContainer = document.getElementById('agentContainer');
foldspace('when', 'ready', () => {
foldspace.agent({
apiName: AGENT_API_NAME,
mode: 'EMBEDDED',
configuration: {
embeddedConfiguration: {
container: agentContainer
}
}
});
});
```
## Embedded configuration
[Section titled “Embedded configuration”](#embedded-configuration)
The `embeddedConfiguration` object controls how the agent renders inside your application. The property relevant to initialization:
| Property | Type | Required | Default | Description |
| :-------- | :------------ | :------- | :------ | :----------------------------------------------------------------------------------------------- |
| container | `HTMLElement` | Yes | - | The DOM element where the embedded agent will be mounted. Must be an existing element reference. |
Note
For all other appearance, layout, and behavioral options, visit the full configuration reference.
Tip
Configure the agent’s look and feel in **Agent Studio** > **Style & Behavior**.
## Next steps
[Section titled “Next steps”](#next-steps)
* [Actions](/guides/connecting-actions/): give the agent AI-Native actions that run workflows and return context-aware responses.
* [Knowledge Base](/user-guides/knowledge-base/): connect a Knowledge Base for grounded answers.
# Execute actions
> Write the execute() handler that runs your action's logic and returns data the agent uses in its next reply.
`execute` is the code behind an action. When the agent invokes the action, it awaits your `execute(params)` function and uses whatever you return in its next chat message. There’s no UI — just logic that runs and data that comes back.
Reach for this whenever the result is information the agent should reason over or relay: creating a record, calling an API, looking up a value. To draw an interactive component in the chat instead, see [UI components in chat](/guides/in-chat-ui/) under **Agentic UI**.
Prerequisites
You’ve [defined an action](/user-guides/authoring-actions/) in Agent Studio and [connected a handler](/guides/connecting-actions/) for it.
## Action handler options
[Section titled “Action handler options”](#action-handler-options)
| Property | Type | Default | Required | Description |
| :--------------- | :----------------------------------------------- | :------ | :------: | :-------------------------------------------------------------------------------------------------------------------------------------- |
| `execute` | `(params: any) => Promise` | N/A | Yes | Handler for your async task; must return a `Promise` with data. |
| `render` | `(data, host, header, callback, cancel) => void` | N/A | No | Optional. Draw an interactive component in the chat from the data `execute` returns — see [UI components in chat](/guides/in-chat-ui/). |
| `awaitUserInput` | `boolean` | false | No | When `true`, the agent will pause and wait for user interaction before continuing. |
| `timeout` | `number` | 5000 | No | Milliseconds to abort the `execute` call (default `5000`). |
```
sequenceDiagram
participant U as User
participant A as Agent
participant H as execute(params)
participant API as Your API
U->>A: Natural-language request
A->>A: Extract params from intent
A->>H: Call action handler
H->>API: fetch / POST / query
API-->>H: Response data
H-->>A: Return result object
A->>U: Chat message using returned data
```
## Example
[Section titled “Example”](#example)
This `create_task` action POSTs the agent-supplied `params` to an API and returns the created record. Return every field the agent might need later in the conversation: here, including a `link` means the agent can answer a follow-up like “What’s the link?” without another call.
```javascript
foldspace("when", "ready", () => {
foldspace.agent({ /* …common setup… */ })
.addActionHandlers({
create_task: {
execute: async (params) => {
const {title, body} = params;
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
body: JSON.stringify({
title,
body,
userId: 'playground',
}),
headers: {
'Content-type': 'application/json; charset=UTF-8',
},
});
const data = await response.json()
// Return all relevant fields so the agent can reference them later
return {
id: data.id,
title: data.title,
body: data.body,
userId: data.userId,
// Include a link property—if the user asks "What's the link?", the agent can supply this
link: `https://jsonplaceholder.typicode.com/posts/${data.id}`
};
}
}
});
});
```
## Related
[Section titled “Related”](#related)
* [UI components in chat](/guides/in-chat-ui/): render an interactive component in the chat instead of returning plain data.
* [Connecting actions in your application](/guides/connecting-actions/): wire an action key and schema into your product code.
# UI components in chat
> Let an action render an interactive component — a form, card, or confirmation — directly in the chat, then send the user's input back to the agent.
Most actions return data the agent relays as text. An action that renders a **UI component in the chat** goes one step further: after it runs, it draws an interactive component — a form, a card, a confirmation dialog — directly into the chat. The user acts on it inline, and their input flows back to the agent.
An action and its UI are two halves of one feature. `execute(params)` owns the **logic** — fetch or write data. `render(data)` owns the **presentation** — build the component the user sees and interacts with. Together they make up an *execute-and-render* action.
## When to use it
[Section titled “When to use it”](#when-to-use-it)
* **Show** data the user should take in at a glance — a table, a summary card, a dashboard.
* **Collect** input without leaving the chat — a form, a picker, an editable record.
* **Confirm** a consequential change before it happens — an approve / reject dialog.
When you only need data back as text, with no UI, use a plain [execute action](/guides/executing-actions/) instead.
## How it works
[Section titled “How it works”](#how-it-works)
When `execute(params)` resolves, the agent hands its result to your `render` function. You build DOM nodes into the container you’re given; when the user submits, you call `callback(value)` to send their input back to the agent, or `cancel()` to abort.
```
sequenceDiagram
participant U as User
participant A as Agent
participant E as execute(params)
participant R as render(data, host, ...)
U->>A: Natural-language request
A->>E: Call execute handler
E-->>A: Return data object
A->>R: Pass data to render
R->>R: Build the UI component
R->>U: Interactive component in chat
U->>R: Interact (edit, confirm)
R->>A: callback(value)
A->>U: Continue with the submitted data
```
Note
For a component that takes input or a confirmation, set `awaitUserInput: true` so the agent pauses until the user submits or cancels.
## Render options
[Section titled “Render options”](#render-options)
Your `render` function receives these arguments, in order.
| Parameter | Type | Description |
| ---------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `data` | `any` | The value returned by your `execute` handler (e.g., an object or array of results). |
| `host` | `HTMLElement` | The container element where you append your UI nodes (forms, cards, lists). |
| `header` | `HTMLElement` | A header element you can style or populate with a title, instructions, or status. |
| `callback` | `(value: string, disableOnSubmit?: boolean) => void` | Call this when the user submits; the value is passed to the agent. `disableOnSubmit` defaults to `true` (the SDK disables your UI after submit) — set it to `false` to keep the component interactive. |
| `cancel` | `() => void` | Call this if the user aborts, so the agent resumes without new data. A default “Cancel” control is also rendered. |
## Example
[Section titled “Example”](#example)
This `show_task` action fetches a task in `execute`, then renders an editable card in `render`. `awaitUserInput: true` pauses the agent until the user saves or cancels: **Save** passes the edited fields to `callback`, and **Cancel** aborts.
```javascript
foldspace("when", "ready", () => {
foldspace.agent({ /* …common setup… */ })
.addActionHandlers({
show_task: {
// 1. Fetch the task by ID
execute: async (params) => {
const { taskId } = params;
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${taskId}`
);
const data = await response.json();
// Return the task object to both agent and renderer
return data;
},
// 2. Tell the agent to wait for user input before continuing
awaitUserInput: true,
// 3. Render an editable card UI
render: (task, host, header, callback, cancel) => {
const style = document.createElement('style');
style.textContent = `
.task-card {
border: 1px solid #e3e3e3;
border-radius: 8px;
margin-top: 8px;
font-family: sans-serif;
overflow: hidden;
}
.task-card-header {
background-color: #f5f5f5;
padding: 12px 16px;
font-weight: bold;
}
.task-card-body {
padding: 16px;
}
.task-card-body label {
display: block;
margin-bottom: 4px;
font-size: 14px;
}
.task-card-body input,
.task-card-body textarea {
width: 100%;
margin-bottom: 12px;
padding: 8px;
box-sizing: border-box;
font-size: 14px;
}
.task-card-footer {
padding: 12px 16px;
text-align: right;
background-color: #fafafa;
}
.task-card-footer button {
margin-left: 8px;
padding: 8px 16px;
cursor: pointer;
}
`;
header.appendChild(style);
// Build the card container
const card = document.createElement('div');
card.className = 'task-card';
// Header section (title)
const cardHeader = document.createElement('div');
cardHeader.className = 'task-card-header';
cardHeader.textContent = `Edit Task #${task.id}`;
// Body section (form fields)
const body = document.createElement('div');
body.className = 'task-card-body';
const titleLabel = document.createElement('label');
titleLabel.textContent = 'Title:';
const titleInput = document.createElement('input');
titleInput.type = 'text';
titleInput.value = task.title;
const bodyLabel = document.createElement('label');
bodyLabel.textContent = 'Body:';
const bodyInput = document.createElement('textarea');
bodyInput.rows = 4;
bodyInput.value = task.body;
body.append(titleLabel, titleInput, bodyLabel, bodyInput);
// Footer section (buttons)
const footer = document.createElement('div');
footer.className = 'task-card-footer';
const saveBtn = document.createElement('button');
saveBtn.textContent = 'Save';
saveBtn.onclick = () => {
callback({
id: task.id,
title: titleInput.value,
body: bodyInput.value,
});
};
const cancelBtn = document.createElement('button');
cancelBtn.textContent = 'Cancel';
cancelBtn.onclick = cancel;
footer.append(cancelBtn, saveBtn);
// Assemble and mount
card.append(cardHeader, body, footer);
host.appendChild(card);
}
}
});
});
```
## Related
[Section titled “Related”](#related)
* [Execute actions](/guides/executing-actions/): return data to the agent without rendering UI.
* [What are Actions](/guides/text-driven-actions/): the action model these build on.
# Connecting Foldspace to Intercom: Automated Support Handoff
> Step-by-step walkthrough for implementing an automated handoff from your Foldspace Agent to a live Intercom support session.
## Overview
[Section titled “Overview”](#overview)
By implementing this action, your Foldspace Agent can intelligently recognize when it has reached its limits and seamlessly transition the conversation to your human support team. This ensures that frustrated users or complex technical issues are always handled with the highest level of care.
There are two ways to hand off to Intercom. Pick based on how your team works:
| Handoff type | What happens | Use when |
| :----------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------ |
| **Live chat** | The Agent opens the Intercom Messenger on the page, seeded with the conversation summary, then steps aside. | You staff the Intercom Inbox for live chat. |
| **Email → conversation** | The Agent emails the summary and context to your Intercom-connected support address, which opens a conversation in the Inbox. | You handle support asynchronously. |
Both are triggered the same way; only the destination differs. The steps below cover the live-chat handoff — the email option is at the end of this guide.
Leave the built-in handoff off
This handoff is driven by your own `support_handoff` action, not by Foldspace’s built-in escalation. Keep **Offer support handoff** OFF in **Style & Behavior → Data Access & Handoff** so the agent escalates through your handler rather than the built-in ticket channel. The toggle belongs to the email path only.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* An active Intercom subscription with the Intercom Messenger installed on your site.
* Access to your Foldspace Dashboard.
* The Foldspace Web SDK integrated into your web application.
## Step 1: Create the “Support Handoff” Action
[Section titled “Step 1: Create the “Support Handoff” Action”](#step-1-create-the-support-handoff-action)
First, we need to define the action inside the Foldspace App so the Agent knows how to “call” for human help.
1. Log in to your Foldspace App.
2. Navigate to the **Actions** section and click **Create New Action**.
3. Fill in the fields below. Each one has a **Copy** button, so you can paste it straight into the form.
Let the Foldspace assistant build it Copy prompt
The Actions editor has a Foldspace assistant beside the form. Paste this prompt into it and it fills in every field below for you, then leaves the action as a draft for you to review before publishing.
Create a new action for this agent with exactly these values. Name: Support Handoff Action key: support\_handoff Description: Triggers a live support hand-off via Intercom when the Agent cannot resolve an issue, detects a bug, or the user is frustrated. Instructions: Use this action ONLY when: - The user explicitly asks for a human. - You have failed to find an answer after multiple attempts. - The user mentions a 'bug' or technical error. - The user's sentiment is highly frustrated. You must synthesize a clear, one-sentence summary of the user's problem for the 'user\_prompt' property. User input, one field: - Name: user\_prompt - Type: string - Required: yes - Description: A concise summary of the user's issue and why the hand-off is being triggered (e.g. 'User is unable to save settings'). A front-end action handler in my own app runs this one: it opens the Intercom Messenger seeded with the summary. There is no API call and no server-side execution to configure. Save it as a draft so I can review it before publishing.
### Fields
[Section titled “Fields”](#fields)
Name Copy
Support Handoff
Action key Copy
support\_handoff
Description Copy
Triggers a live support hand-off via Intercom when the Agent cannot resolve an issue, detects a bug, or the user is frustrated.
### Logic & Instructions
[Section titled “Logic & Instructions”](#logic--instructions)
Paste this into the **Instructions** field. It stops the Agent from escalating on ordinary questions:
Instructions Copy
Use this action ONLY when: - The user explicitly asks for a human. - You have failed to find an answer after multiple attempts. - The user mentions a 'bug' or technical error. - The user's sentiment is highly frustrated. You must synthesize a clear, one-sentence summary of the user's problem for the 'user\_prompt' property.
### User Input Configuration
[Section titled “User Input Configuration”](#user-input-configuration)
Create a required input field so the Support Agent receives context immediately upon handoff. Set **Type** to `string` and **Required** to yes.
Input name Copy
user\_prompt
Input description Copy
A concise summary of the user's issue and why the hand-off is being triggered (e.g. 'User is unable to save settings').
## Step 2: Code Implementation
[Section titled “Step 2: Code Implementation”](#step-2-code-implementation)
Once the action is defined in the dashboard, you must handle the execution on your front-end. Add the following code to your application where you initialize the Foldspace Agent:
```javascript
window.foldspace("when", "ready", () => {
const agent = window.foldspace.agent("planning-copilot");
agent.addActionHandlers({
support_handoff: {
execute: async (params) => {
try {
const HANDOFF_DELAY_MS = 5000; // 5-second delay for smooth UX
// Verify Intercom is loaded on the page
if (!window.Intercom) {
return {
message:
"Sorry, I'm unable to connect to the Support Agent right now. How else can I assist you?",
};
}
const message = params?.user_prompt?.trim() || "User requested human assistance.";
// Trigger the handoff sequence
setTimeout(() => {
window.Intercom("showNewMessage", message);
agent.hide(); // Hide the Foldspace agent to let Intercom take over
}, HANDOFF_DELAY_MS);
return {
message:
"Transferring you to a Support Agent now. One moment please.",
};
} catch (error) {
return {
message:
"Something went wrong while connecting to support. Please try again or reach out directly.",
};
}
},
},
});
agent.show();
});
```
## Email handoff (opens an Intercom conversation)
[Section titled “Email handoff (opens an Intercom conversation)”](#email-handoff-opens-an-intercom-conversation)
When you don’t staff live chat, the Agent can hand off by **email** instead. This isn’t a real-time transfer — Foldspace sends the conversation summary and context to your support address, and Intercom turns that email into a conversation in the Inbox.
No custom code required:
1. Enable handoff in **Style & Behavior → Data Access & Handoff** ([Style & Behavior](/user-guides/building-your-agent/)).
2. Set the destination to **Email** in [Integrations](/user-guides/integrations/).
3. Point it at an address that reaches your **Intercom Inbox**. Intercom only receives mail sent to your workspace’s inbound address — `[app_id]@[workspace].intercom-mail.com` — so either send there directly, or send to a support address you’ve configured to auto-forward to it (**Settings → Channels → Email → Domains & addresses**).
**Don’t send to a plain mailbox.** An address that doesn’t forward to your workspace’s inbound address never appears in the Intercom Inbox, so the escalation sits in someone’s email — unassigned, unrouted, and untracked.
## Best Practices
[Section titled “Best Practices”](#best-practices)
* **User Expectations:** The 5-second delay defined in `HANDOFF_DELAY_MS` gives the user time to read the Agent’s confirmation message before the UI switches.
* **Fallback:** If the Intercom script fails to load (e.g., due to an ad-blocker), the code includes a fallback message to ensure the user isn’t left in a “dead end.”
* **Context is King:** The `user_prompt` is automatically sent to Intercom, meaning your support team starts the conversation with a summary of the issue already in hand.
## Related
[Section titled “Related”](#related)
* [Connecting Foldspace to Zendesk](/guides/zendesk-handoff/) — the same live-handoff pattern for Zendesk, plus an email-to-ticket option.
* [Integrations](/user-guides/integrations/) — where handoff destinations (email, HubSpot, webhook) are configured.
## Intercom references
[Section titled “Intercom references”](#intercom-references)
Official Intercom documentation for the APIs and setup this guide relies on:
* [JavaScript API: Methods](https://developers.intercom.com/installing-intercom/web/methods) — `Intercom("showNewMessage", prePopulatedContent)` and the other Messenger methods.
* [Installing the Intercom Messenger for web](https://developers.intercom.com/installing-intercom/web/installation) — get the Messenger snippet onto your page so `window.Intercom` is available.
* [Get started with Intercom Inbox](https://www.intercom.com/help/en/articles/6274899-get-started-with-intercom-inbox) — the Inbox that handles inbound support email.
* [Forward your email to your team inbox](https://www.intercom.com/help/en/articles/6522819-forward-your-email-to-your-team-inbox-using-the-next-gen-inbox) — the workspace inbound address and forwarding setup.
# A2A / MCP Protocols
> Connect the agent to external tools, data, and other agents over MCP and A2A.
Two protocols extend the agent beyond your own app:
* **MCP** (Model Context Protocol) gives the agent tools and data from an MCP server.
* **A2A** (Agent-to-Agent) lets the agent delegate to, and receive tasks from, other AI agents.
## MCP
[Section titled “MCP”](#mcp)
[MCP server integration ](/reference/mcp-server/)Connect an MCP server so the agent can use its tools and data.
## A2A
[Section titled “A2A”](#a2a)
[A2A server agents ](/guides/a2a-server-agents/)Expose your agent as an A2A server.
[A2A quickstart ](/guides/a2a-quickstart/)The fastest path to a working A2A connection.
[A2A advanced patterns ](/guides/a2a-advanced/)Streaming, multi-agent, and other advanced flows.
[Connect an A2A agent ](/guides/a2a-connect/)Delegate to an external A2A agent from yours.
## Support-desk handoff
[Section titled “Support-desk handoff”](#support-desk-handoff)
The code for handing a conversation to a human desk also lives here:
[Intercom handoff ](/guides/intercom-handoff/)Hand off into a live Intercom session.
[Zendesk handoff ](/guides/zendesk-handoff/)Hand off into Zendesk chat or a ticket.
## Where to start
[Section titled “Where to start”](#where-to-start)
For tools and data, start with [MCP server integration](/reference/mcp-server/). For agent-to-agent, start with the [A2A quickstart](/guides/a2a-quickstart/).
# Agentic UI
> The UI components the agent renders and drives, plus end-to-end use cases you can copy from.
Agentic UI is how the agent operates your interface: the components it renders and drives inside the conversation, plus worked end-to-end use cases you can adapt.
## Interaction modalities
[Section titled “Interaction modalities”](#interaction-modalities)
Users interact with the agent in more than one way, and an action can respond in whichever fits the moment — or a combination:
* **Text** — the default chat conversation.
* **Voice** — spoken interaction; configure it in [Voice](/user-guides/voice/).
* **Visual** — interactive UI components (Chatterblocks) rendered in the chat; see [UI components in chat](/guides/in-chat-ui/).
* **Combination** — mix them, for example a spoken request that returns a visual component to act on.
## Agentic UI components
[Section titled “Agentic UI components”](#agentic-ui-components)
The building blocks the agent renders and drives inside your product.
[UI components in chat ](/guides/in-chat-ui/)Render forms, cards, and dashboards (Chatterblocks) inline in the conversation.
[Shared State (Tandem) ](/guides/shared-state/)Keep the agent and your live page in sync so responses match what the user sees.
### Shared State examples
[Section titled “Shared State examples”](#shared-state-examples)
[React meeting form ](/guides/shared-state-react-meeting/)Two-way sync between the agent and a React form.
[React filter panel ](/guides/shared-state-react-filter/)The agent drives a filter panel as the user browses.
[Backbone ](/guides/shared-state-backbone/)Shared State wired into a Backbone app.
[Vanilla JS ](/guides/shared-state-vanilla/)Shared State with no framework.
## Use cases
[Section titled “Use cases”](#use-cases)
End-to-end walkthroughs that put the pieces together.
[Analyze report data ](/guides/task-agent-analysis/)Run a task agent over report data and render the result inline.
[Embed the agent ](/guides/embedded-agent/)Run the agent as an embedded, always-on surface in your app.
Tip
Want to start from a prompt instead of a pattern? See [Vibe coding](/guides/vibe-coding/).
# Shared State (Tandem)
> Sync your app's UI state with the agent in real time for two-way, state-aware experiences.
## What is Shared State?
[Section titled “What is Shared State?”](#what-is-shared-state)
While AI Actions let the agent perform tasks, Shared State gives the agent eyes and hands inside your application. It creates a two-way sync between your app’s UI state (like a form or settings panel) and the agent. The agent reads the current state of your UI in real time and writes changes back to it.
## Why use Shared State?
[Section titled “Why use Shared State?”](#why-use-shared-state)
* **Contextual awareness** — the agent sees exactly what the user sees, so it can give relevant help without asking clarifying questions.
* **Interactive guidance** — instead of just describing steps, the agent can highlight fields, pre-fill values, and walk users through complex workflows.
* **Seamless task automation** — the agent can update your UI directly, turning multi-step processes into a single conversation.
## How it works: a two-way street
[Section titled “How it works: a two-way street”](#how-it-works-a-two-way-street)
* **Your App → Agent**: your app calls `shareState()` whenever the UI changes, keeping the agent’s picture of the current screen up to date.
* **Agent → Your App**: when the agent decides to act, it pushes updates through the handler you registered, and your UI re-renders instantly.
```
flowchart LR
subgraph Your App
UI[UI Component]
SF["shareState(key, state)"]
end
subgraph Foldspace
AG[Agent]
end
UI -- "User types ➜ setForm()" --> SF
SF -- "State snapshot" --> AG
AG -- "Agent updates ➜ handler(key, next)" --> UI
```
Note
This page assumes you’ve already initialized your agent.
## Methods
[Section titled “Methods”](#methods)
Two methods on the agent instance manage the whole process.
### `shareState(key, state, handler, stateDescription)`
[Section titled “shareState(key, state, handler, stateDescription)”](#sharestatekey-state-handler-statedescription)
Establish and update synchronized state. Call it once on initialization, then again every time the user-side state changes.
| Parameter | Type | Required | Description |
| :------------------- | :-------------------------------------- | :------: | :---------------------------------------------------------------------------------------------------------------------------------------------- |
| `key` | `string` | Yes | A unique string identifier for this piece of state (e.g. `"contact_form"`). |
| `state` | `object` | Yes | A snapshot of the current UI state object (e.g. `{ name: "", email: "" }`). |
| `handler` | `(key: string, nextState: any) => void` | No | Optional, but required to let the agent push updates back to your UI. Pass it for two-way sync. |
| `stateDescription` | `string` | No | A free-form string that adds context about the state. Can describe the type/model, usage instructions, or provide a human-readable explanation. |
| `persistAcrossPages` | `boolean` | No | |
```javascript
// Initial sync: Register the state key, initial data, and the handler
const agentKey = "YOUR_AGENT_KEY";
const agent = window.foldspace?.agent(agentKey);
agent.current?.shareState(stateKey, form, handleStateChange, stateDescription);
```
#### Guide the agent with `stateDescription`
[Section titled “Guide the agent with stateDescription”](#guide-the-agent-with-statedescription)
`stateDescription` is optional but gives context to both developers and the agent. Though concise by design, it can carry different kinds of guidance:
* **Naming conventions**: e.g. `"All keys in this state should be snake_case"`.
* **Model structure hints**: e.g. `"If 'items' is an empty array, each entry should contain { id: string, quantity: number }"`.
* **Purpose description**: e.g. `"Tracks the shipping address details required for checkout"`.
`stateDescription` enriches the state with descriptive or instructional metadata, so the UI and the agent interpret the state consistently.
### `clearState(key)`
[Section titled “clearState(key)”](#clearstatekey)
Tell the agent to stop tracking a piece of state. Call it for cleanup when a component unmounts or the user navigates away, preventing memory leaks.
| Parameter | Type | Required | Description |
| :-------- | :------- | :------: | :------------------------------------------------------------ |
| `key` | `string` | Yes | The unique string identifier for the state you wish to clear. |
## Sync a React form
[Section titled “Sync a React form”](#sync-a-react-form)
Synchronize a React form component’s state with the agent using hooks.
```typescript
import { useEffect, useRef, useState } from "react";
type FormState = {
name: string;
email: string;
company?: string;
};
interface Props {
agentKey: string;
}
export default function SharedStateForm({ agentKey }: Props) {
// 1. Local UI state is kept minimal.
const [form, setForm] = useState({ name: "", email: "", company: "" });
// 2. The agent instance is created once and stored in a ref.
const agentRef = useRef(null);
const stateKey = "contact_form";
const stateDescription = `
type FormState = {
name: string;
email: string;
company?: string;
};
interface Props {
agentKey: string;
}`;
// 3. `useEffect` handles the entire lifecycle: setup, subscription, and cleanup.
useEffect(() => {
// Initialize the agent
agentRef.current = window.foldspace?.agent(agentKey);
// Agent -> UI: Define the handler to receive updates FROM the agent
const handleStateChange = (key: string, nextState: unknown) => {
if (key === stateKey) {
setForm(nextState as FormState); // Update local state
}
};
// Initial sync: Register the state key, initial data, and the handler
agentRef.current?.shareState(stateKey, form, handleStateChange, stateDescription);
// Cleanup function: Tell the agent to stop tracking on unmount
return () => {
agentRef.current?.clearState(stateKey);
agentRef.current = null;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [agentKey]);
// 4. UI -> Agent: Call this on user input to keep the agent's snapshot fresh.
const updateField = (key: K, value: FormState[K]) => {
const next = { ...form, [key]: value };
setForm(next);
// Send the fresh snapshot to the agent
agentRef.current?.shareState(stateKey, next);
};
// 5. Render your form inputs and call updateField on change.
return <>...>;
}
```
```
sequenceDiagram
participant App as Your App
participant SDK as Foldspace SDK
participant Agent as Agent
App->>SDK: shareState("form", data, handler)
SDK->>Agent: State snapshot
Note right of Agent: Agent reads form context
Agent-->>SDK: Updated state
SDK-->>App: handler("form", nextState)
App->>App: setForm(nextState)
```
## Related
[Section titled “Related”](#related)
* [AI Actions](./actions-1): give your agent custom actions that automate workflows and return context-aware responses.
* [Knowledge Base](./knowledge-base): connect a knowledge base for accurate, context-rich answers.
# Backbone 'Reporting Filter' Shared State Example
> Integrate Foldspace Shared State in a Backbone app to sync filter data with your agent in real time for interactive, context-driven experiences.
```javascript
// Bind Backbone to jQuery (defensive)
if (!Backbone.$ && window.jQuery) Backbone.$ = window.jQuery;
// --- Share-state Backbone example (filters) ---
const stateKey = "bb_filters";
const agentKey = "YOUR_AGENT_KEY";
const agent = window.foldspace?.agent?.(agentKey);
const FiltersModel = Backbone.Model.extend({
defaults: { location: "", account: "" }
});
const FiltersView = Backbone.View.extend({
el: "#filters-root",
className: "filters-panel",
template: _.template(`
`),
events: {
"change select[name=location]": "onChange",
"change select[name=account]" : "onChange"
},
initialize() {
this.listenTo(this.model, "change", this.renderSnapshot);
// Agent -> UI: subscribe for shared state updates
this._handler = (key, nextState) => {
if (key === stateKey) this.model.set(nextState);
};
agent?.shareState(stateKey, this.model.toJSON(), this._handler);
this.render(); // ensure element exists & template rendered
},
render() {
this.$el.html(this.template());
// Sync current values into selects
this.$("select[name=location]").val(this.model.get("location"));
this.$("select[name=account]").val(this.model.get("account"));
this.renderSnapshot();
return this;
},
renderSnapshot() {
this.$(".snapshot").text(JSON.stringify(this.model.toJSON()));
},
onChange(e) {
const $t = this.$(e.currentTarget);
const next = { ...this.model.toJSON(), [$t.attr("name")]: $t.val() };
this.model.set(next);
// UI -> Agent
agent?.shareState(stateKey, next);
this.renderSnapshot();
},
remove() {
agent?.clearState(stateKey);
Backbone.View.prototype.remove.call(this);
}
});
// Mount after #filters-root exists
new FiltersView({ model: new FiltersModel() });
```
# React 'Reporting Filter' Shared State Example
> Learn how to sync a reporting filter form with your Foldspace agent using React hooks and Shared State for real-time, context-aware data filtering.
Shared State example of a ‘Reporting Filter’ React form with the agent using hooks.
FilterPanel.tsx
```typescript
import { useEffect, useRef, useState } from "react";
type FilterState = {
location: string; // e.g., "NYC"
account: string; // e.g., "Acme Inc"
};
interface Props {
agentKey: string;
initial?: Partial;
}
export default function FilterPanel({ agentKey, initial }: Props) {
const [filters, setFilters] = useState({
location: initial?.location ?? "",
account: initial?.account ?? "",
});
const agentRef = useRef(null);
const stateKey = "filters_panel";
const stateDescription = `
type FilterState = {
location: string; // e.g., "NYC"
account: string; // e.g., "Acme Inc"
};
interface Props {
agentKey: string;
initial?: Partial;
}`;
useEffect(() => {
agentRef.current = (window as any).foldspace?.agent(agentKey);
const handleStateChange = (key: string, nextState: unknown) => {
if (key === stateKey) setFilters(nextState as FilterState);
};
// initial sync
agentRef.current?.shareState(stateKey, filters, handleStateChange, stateDescription);
return () => {
agentRef.current?.clearState(stateKey);
agentRef.current = null;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [agentKey]);
const updateField = (k: K, v: FilterState[K]) => {
const next = { ...filters, [k]: v };
setFilters(next);
agentRef.current?.shareState(stateKey, next);
};
const LOCATIONS = ["NYC", "London", "Tel Aviv", "Remote"];
const ACCOUNTS = ["Acme Inc", "Globex", "Initech", "Umbrella"];
return (
{/* The agent now has a fresh snapshot at state key "filters_panel". */}
Current: {JSON.stringify(filters)}
);
}
```
# React 'Schedule a Meeting' Shared State Example
> See a Shared State example in React. Learn how to sync meeting details with your agent in real time for interactive, context-aware automation.
Shared State example of a ‘Schedule Meeting’ React form with the agent using hooks.
ScheduleMeetingForm.tsx
```typescript
import { useEffect, useRef, useState } from "react";
type MeetingState = {
title: string;
date: string; // "2025-09-01"
startTime: string; // "14:00"
endTime: string; // "15:00"
attendees: string; // comma-separated emails
location: string; // "Zoom" | "Office TLV" | "Google Meet" | custom
notes?: string;
};
interface Props {
agentKey: string;
initial?: Partial;
}
export default function ScheduleMeetingForm({ agentKey, initial }: Props) {
const [form, setForm] = useState({
title: initial?.title ?? "",
date: initial?.date ?? "",
startTime: initial?.startTime ?? "",
endTime: initial?.endTime ?? "",
attendees: initial?.attendees ?? "",
location: initial?.location ?? "",
notes: initial?.notes ?? "",
});
const agentRef = useRef(null);
const stateKey = "meeting_form";
const stateDescription = `
type MeetingState = {
title: string;
date: string; // "2025-09-01"
startTime: string; // "14:00"
endTime: string; // "15:00"
attendees: string; // comma-separated emails
location: string; // "Zoom" | "Office TLV" | "Google Meet" | custom
notes?: string;
};
interface Props {
agentKey: string;
initial?: Partial;
}`;
useEffect(() => {
agentRef.current = (window as any).foldspace?.agent(agentKey);
const handleStateChange = (key: string, nextState: unknown) => {
if (key === stateKey) setForm(nextState as MeetingState);
};
agentRef.current?.shareState(stateKey, form, handleStateChange, stateDescription);
return () => {
agentRef.current?.clearState(stateKey);
agentRef.current = null;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [agentKey]);
const updateField = (k: K, v: MeetingState[K]) => {
const next = { ...form, [k]: v };
setForm(next);
agentRef.current?.shareState(stateKey, next);
};
return (
);
}
```
# Vanilla Shared State Example
> Synchronize standard HTML form inputs with Foldspace Shared State without a framework.
## Vanilla JS Example
[Section titled “Vanilla JS Example”](#vanilla-js-example)
This example demonstrates how to synchronize standard HTML form inputs without a framework.
```javascript
```
# Example: analyze report data
> Send report data to a task agent from code and get structured insights back — in complete or streaming mode, with caching to cut cost.
A common use for [task agents](/reference/task-agent-api/) is turning raw report data into insight: summarize a sales export, flag anomalies in a usage report, or classify rows of feedback. You configure the task agent once in **Agent Studio → Task Agents** (instructions, input expectations, and an output schema), then call it from code with `runTask()`.
This page assumes a published task agent with the key `report_analyzer` whose output is configured as **JSON**.
## Structured analysis (complete mode)
[Section titled “Structured analysis (complete mode)”](#structured-analysis-complete-mode)
Pass your report rows and a question as `data`. Because the task agent returns `JSON`, `runTask()` resolves with an object shaped by the output schema you defined in Studio.
```javascript
foldspace('when', 'ready', async () => {
// Your report — rows pulled from your own API, warehouse, or export.
const report = [
{ region: 'NA', revenue: 18200, refunds: 320, signups: 142 },
{ region: 'EU', revenue: 12750, refunds: 1180, signups: 98 },
{ region: 'APAC', revenue: 9400, refunds: 210, signups: 67 },
];
try {
const analysis = await foldspace
.agent('YOUR-AGENT-API-NAME')
.runTask({
taskKey: 'report_analyzer',
data: {
report,
question: 'Summarize the top revenue drivers and flag any anomalies.',
},
});
// `analysis` matches your configured output schema, e.g.:
// { summary: string, anomalies: Array<{ region, metric, note }>, topRegion: string }
console.log(analysis.summary);
renderAnomalies(analysis.anomalies);
} catch (error) {
console.error('Report analysis failed:', error);
}
});
```
## Narrative summary (streaming mode)
[Section titled “Narrative summary (streaming mode)”](#narrative-summary-streaming-mode)
For a long, written analysis you want to show as it’s generated, configure the task agent’s response as **TEXT** and pass `streamOptions`. Each chunk arrives through `onMessage`, so you can append to the UI progressively.
```javascript
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'report_analyzer',
data: { report, question: 'Write an executive summary of this quarter.' },
streamOptions: {
onStart: ({ abortStreamTask }) => {
// Keep the handle so you can cancel a long run if the user navigates away.
currentAbort = abortStreamTask;
},
onMessage: ({ textDelta, fullText }) => {
appendToReport(textDelta); // or re-render with fullText
},
onComplete: ({ fullText }) => {
finalizeReport(fullText);
},
onError: (error) => {
showError(error);
},
},
});
```
Note
Streaming is only supported for `TEXT` task agents. Keep a JSON output type when you need a structured object back.
## Cache repeated analyses
[Section titled “Cache repeated analyses”](#cache-repeated-analyses)
The same report analyzed twice should not pay for the model twice. Task agents cache results automatically, so an identical `data` payload returns from cache. Tune it per call with `cacheOptions`:
```javascript
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'report_analyzer',
data: { report, question: 'Summarize the top revenue drivers.' },
cacheOptions: {
// Re-use a cached analysis for an hour; dashboards that refresh often stay cheap.
ttlSeconds: 3600,
// Set bypass: true to force a fresh run when the underlying data changed.
bypass: false,
},
});
```
See the [Task Agent API reference](/reference/task-agent-api/#cache-options) for the full caching behavior, and the [Task Agents guide](/user-guides/task-agents/) for configuring the agent, its output schema, and reviewing model, latency, and cost in the logs.
# Text Driven Actions
> How Text Driven Actions let your Foldspace agent execute tasks, fetch live data, and render interactive UI components directly inside conversations.
## What are Actions?
[Section titled “What are Actions?”](#what-are-actions)
AI Actions are custom functions that give your Foldspace agent the ability to take actions in your platform. Think of them as creating API endpoints for your AI, allowing it to connect directly to your backend services and third-party APIs.
This is the core mechanism that allows your agent to act, not just talk. Instead of only answering questions based on a knowledge base, it can perform tasks, fetch live data, and automate workflows that are unique to your platform. The primary benefit is giving users instant access to information and functionality, saving them from having to navigate complex menus and dashboards.
## Types of Actions
[Section titled “Types of Actions”](#types-of-actions)
You can empower your agent with two primary types of actions, depending on your use case.
### Execute-Only Actions
[Section titled “Execute-Only Actions”](#execute-only-actions)
Use these when you need the agent to perform a task or fetch data and then use that information to formulate a natural language response. This is perfect for GET requests or simple POST operations.
**Example:** A user asks, “What’s the status of my support ticket?” The action fetches the status, and the agent replies, “Your ticket is currently ‘In Progress’.”
### Execute + Render Actions
[Section titled “Execute + Render Actions”](#execute--render-actions)
Use this when you want to display a custom, interactive UI component directly in the conversation — a form, card, or confirmation the user can act on without leaving the chat.
**Example:** A user asks, “Show me the details for task #42.” The action fetches the task data and renders an editable card where the user can update the title or description and save their changes.
## Next Steps
[Section titled “Next Steps”](#next-steps)
Ready to build? An action’s logic and its UI are configured separately. Dive into the implementation guides below.
* [Execute actions](/guides/executing-actions/) — run logic and return data to the agent.
* [UI components in chat](/guides/in-chat-ui/) — render an interactive component in the chat.
# Vibe Coding
> Integrate Foldspace from your AI coding tool — install the plugin, add the public Docs MCP for instant documentation search, or connect the Product MCP to Cursor, Claude Code, Windsurf, and VS Code to build AI Actions in code.
Wire Foldspace up from the AI coding tool you already use. The fastest path is the Foldspace plugin — your coding agent adds the agent, connects your authenticated users, and discovers your product’s actions for you. Or connect the MCP server directly to your editor to browse and implement AI Actions by hand.
## Set up with your AI coding tool
[Section titled “Set up with your AI coding tool”](#set-up-with-your-ai-coding-tool)
Once you have your Agent Key, skip the manual wiring and let your AI coding tool do the integration:
Set up with your AI coding tool
Install the Foldspace plugin and your AI coding tool will integrate Foldspace for you: add the agent, connect your authenticated users, and discover your product's actions.
* Claude Code
```sh
# Add the Foldspace marketplace (one time)
claude plugin marketplace add foldspace-ai/plugins
# Install the codebase plugin
claude plugin install foldspace-codebase-plugin@foldspace-plugins
```
Then run `/mcp` and pick **foldspace** to sign in — there's no API key to paste. Once you're signed in, ask the agent: *"Integrate Foldspace into this app."*
Full walkthrough, including the remote plugin and how to verify the MCP server: [Install in Claude Code →](/start/claude-code/)
* Cursor
Install the Foldspace plugin from Cursor's plugin interface, then add your API key at your project root:
.env.foldspace
```sh
FOLDSPACE_API_KEY=xxxxxxxx
```
The plugin's MCP server loads the key from this file automatically. See the [plugins repo](https://github.com/foldspace-ai/plugins) for the exact steps.
* Antigravity
Antigravity has no one-click install yet. Open **Settings → Customizations → Add MCP+**, or edit `~/.gemini/config/mcp_config.json`, and add:
mcp\_config.json
```json
{
"mcpServers": {
"foldspace": {
"url": "https://api.foldspace.ai/mcp"
}
}
}
```
[Browse the plugins on GitHub →](https://github.com/foldspace-ai/plugins)
## Add Foldspace to Claude in one click
[Section titled “Add Foldspace to Claude in one click”](#add-foldspace-to-claude-in-one-click)
Using Claude on the web or desktop? Add Foldspace as a custom connector. The link pre-fills the name and URL, so all you do is confirm and authenticate:
One-click install
[**Add the Foldspace connector to Claude →**](https://claude.ai/customize/connectors?modal=add-custom-connector\&connectorName=Foldspace\&connectorUrl=https%3A%2F%2Fapi.foldspace.ai%2Fmcp)
Connector URL: `https://api.foldspace.ai/mcp`
In **Claude Code**, add the same server from the terminal instead:
```bash
claude mcp add --transport http foldspace https://api.foldspace.ai/mcp
```
## Using the Foldspace Model Context Protocol (MCP) servers
[Section titled “Using the Foldspace Model Context Protocol (MCP) servers”](#using-the-foldspace-model-context-protocol-mcp-servers)
The Model Context Protocol (MCP) is a standard for giving AI models real-time, external context. Foldspace exposes **two** MCP servers — add either or both to your editor:
| | Docs MCP | Product MCP |
| -------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Endpoint | `https://docs.foldspace.ai/mcp` | `https://api.foldspace.ai/mcp` |
| Auth | **Public** (no key) | **OAuth** (sign in from your client) |
| Gives your assistant | Semantic search + read access to these docs (`search_docs`, `fetch_page`) | Access to your account’s AI Actions: list them, read schemas, generate handlers |
| Best for | Any developer — ask questions about Foldspace while you code | Building on your own Foldspace agent |
Start with the **Docs MCP** — there’s nothing to configure but a URL. Add the **Product MCP** when you want your assistant to work with your own AI Actions.
## Docs MCP server (public)
[Section titled “Docs MCP server (public)”](#docs-mcp-server-public)
The Docs MCP is read-only and needs no API key. Point any MCP client at `https://docs.foldspace.ai/mcp`. It exposes two tools — `search_docs` (semantic search over the docs) and `fetch_page` (fetch a page as Markdown) — so you can ask your assistant things like *“how do I configure JWT auth in Foldspace?”* and it answers from the current docs.
**Claude Code**
```bash
claude mcp add --transport http foldspace-docs https://docs.foldspace.ai/mcp
```
**Cursor** — Settings → Tools & Integrations → New MCP Server:
```json
{
"mcpServers": {
"foldspace-docs": { "url": "https://docs.foldspace.ai/mcp" }
}
}
```
**Windsurf** — Cascade → hammer icon → Configure → View raw config:
```json
{
"mcpServers": {
"foldspace-docs": { "serverUrl": "https://docs.foldspace.ai/mcp" }
}
}
```
**VS Code (GitHub Copilot)** — in `.vscode/mcp.json`:
```json
{
"servers": {
"foldspace-docs": { "type": "http", "url": "https://docs.foldspace.ai/mcp" }
}
}
```
## Product MCP server (your AI Actions)
[Section titled “Product MCP server (your AI Actions)”](#product-mcp-server-your-ai-actions)
The Product MCP exposes your Foldspace AI Actions as callable tools. Connecting your editor to it lets your AI assistant:
* List all available AI Actions in your Foldspace account.
* Get the detailed schema for any action, including its required parameters.
* Generate boilerplate code for implementing an action handler in your application.
It’s a hosted HTTP server at `https://api.foldspace.ai/mcp`. There’s no API key to paste and nothing to install. The first time your client connects, Foldspace opens a browser window for you to sign in, and the client stores the resulting OAuth token itself.
### Claude
[Section titled “Claude”](#claude)
Use the one-click connector link above, or in Claude Code:
```bash
claude mcp add --transport http foldspace https://api.foldspace.ai/mcp
```
Then run `/mcp` and pick **foldspace** to complete the OAuth sign-in.
### Cursor
[Section titled “Cursor”](#cursor)
Settings → Tools & Integrations → New MCP Server:
```json
{
"mcpServers": {
"foldspace": { "url": "https://api.foldspace.ai/mcp" }
}
}
```
Cursor prompts you to authenticate the first time it connects.
### Windsurf
[Section titled “Windsurf”](#windsurf)
Cascade → hammer icon → Configure → View raw config:
```json
{
"mcpServers": {
"foldspace": { "serverUrl": "https://api.foldspace.ai/mcp" }
}
}
```
### Visual Studio Code (GitHub Copilot)
[Section titled “Visual Studio Code (GitHub Copilot)”](#visual-studio-code-github-copilot)
Open Settings, search for “MCP”, and make sure **GitHub > Copilot > Chat: MCP** is enabled. Then create `.vscode/mcp.json` at your workspace root:
```json
{
"servers": {
"foldspace": { "type": "http", "url": "https://api.foldspace.ai/mcp" }
}
}
```
Reload the window, then use `#` in the Copilot Chat panel to reference the Foldspace tools.
## What you get after connecting
[Section titled “What you get after connecting”](#what-you-get-after-connecting)
Once the connector is authorized, the Foldspace tools show up in your client. Here’s how it looks in Claude’s connector settings:
Search
Settings
General
Account
Privacy
Billing
Usage
Capabilities
Claude Code
Cowork
Customize
Skills
Connectors
Plugins
Connectors
**Foldspace AI** Disconnect
`https://api.foldspace.ai/mcp`
Tool permissions
Choose when Claude is allowed to use these tools.
Read-only tools 18 Needs approval
`discover_actions`
`foldspace_overview`
`generate_action_handler`
`get_action`
`get_action_schema`
`get_action_version`
`get_agent`
`get_agent_install_snippet`
Tools are grouped by what they touch, so you can let read-only calls run freely and keep approval on anything that writes.
# Connecting Foldspace to Zendesk: Support Handoff
> Hand off from your Foldspace Agent to Zendesk — either a live chat handoff into the Zendesk widget, or an email handoff that opens a Zendesk ticket.
## Overview
[Section titled “Overview”](#overview)
When the Foldspace Agent reaches its limits — the user asks for a human, an issue can’t be resolved, or sentiment turns frustrated — it can escalate to your Zendesk support team. The Agent synthesizes a one-sentence summary of the problem and passes it along so the handoff arrives with context, not a cold start.
There are two ways to hand off to Zendesk. Pick based on how your team works:
| Handoff type | What happens | Use when |
| :----------------- | :--------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- |
| **Live chat** | The Agent opens the Zendesk messaging widget on the page and seeds it with the conversation summary, then steps aside. | You run Zendesk **Messaging** or the **Web Widget** and staff live chat. |
| **Email → ticket** | The Agent sends the summary and context to your Zendesk support address, which opens a **ticket**. | You handle support asynchronously through the Zendesk **Agent Workspace**. |
Both are triggered the same way: the user asks to escalate to a human (and the handoff is enabled). The difference is only the destination.
## Live chat handoff
[Section titled “Live chat handoff”](#live-chat-handoff)
This mirrors the [Intercom handoff](/guides/intercom-handoff/): you author a **Support Handoff action** so the Agent knows when to escalate, then implement a front-end handler that drives the Zendesk widget.
Leave the built-in handoff off
This handoff is driven by your own `support_handoff` action, not by Foldspace’s built-in escalation. Keep **Offer support handoff** OFF in **Style & Behavior → Data Access & Handoff** so the agent escalates through your handler rather than the built-in ticket channel. The toggle belongs to the email path only.
### Prerequisites
[Section titled “Prerequisites”](#prerequisites)
* A Zendesk account with **Messaging** or the **Web Widget** installed on your site (so `window.zE` is available).
* Access to your Foldspace Dashboard.
* The Foldspace Web SDK integrated into your web application.
### Step 1: Create the Support Handoff action
[Section titled “Step 1: Create the Support Handoff action”](#step-1-create-the-support-handoff-action)
In your Foldspace App, navigate to **Actions** and click **Create New Action**, then fill in the fields below. Each one has a **Copy** button, so you can paste it straight into the form.
Let the Foldspace assistant build it Copy prompt
The Actions editor has a Foldspace assistant beside the form. Paste this prompt into it and it fills in every field below for you, then leaves the action as a draft for you to review before publishing.
Create a new action for this agent with exactly these values. Name: Support Handoff Action key: support\_handoff Description: Triggers a live support hand-off via Zendesk when the Agent cannot resolve an issue, detects a bug, or the user is frustrated. Instructions: Use this action ONLY when: - The user explicitly asks for a human. - You have failed to find an answer after multiple attempts. - The user mentions a 'bug' or technical error. - The user's sentiment is highly frustrated. You must synthesize a clear, one-sentence summary of the user's problem for the 'user\_prompt' property. User input, one field: - Name: user\_prompt - Type: string - Required: yes - Description: A concise summary of the user's issue and why the hand-off is being triggered (e.g. 'User is unable to save settings'). A front-end action handler in my own app runs this one: it opens the Zendesk messaging widget seeded with the summary. There is no API call and no server-side execution to configure. Save it as a draft so I can review it before publishing.
Name Copy
Support Handoff
Action key Copy
support\_handoff
Description Copy
Triggers a live support hand-off via Zendesk when the Agent cannot resolve an issue, detects a bug, or the user is frustrated.
In the **Instructions** field, constrain when the Agent escalates:
Instructions Copy
Use this action ONLY when: - The user explicitly asks for a human. - You have failed to find an answer after multiple attempts. - The user mentions a 'bug' or technical error. - The user's sentiment is highly frustrated. You must synthesize a clear, one-sentence summary of the user's problem for the 'user\_prompt' property.
Add one required input so the summary travels with the handoff. Set **Type** to `string` and **Required** to yes.
Input name Copy
user\_prompt
Input description Copy
A concise summary of the user's issue and why the hand-off is being triggered (e.g. 'User is unable to save settings').
### Step 2: Code implementation
[Section titled “Step 2: Code implementation”](#step-2-code-implementation)
Add the handler where you initialize the Foldspace Agent. It passes the Agent’s summary to Zendesk as conversation context, opens the widget, then hides the Foldspace panel so Zendesk takes over.
```javascript
window.foldspace("when", "ready", () => {
const agent = window.foldspace.agent("YOUR_AGENT_API_NAME");
agent.addActionHandlers({
support_handoff: {
execute: async (params) => {
try {
const HANDOFF_DELAY_MS = 5000; // let the user read the confirmation first
// Zendesk must be present on the page (Messaging or Web Widget).
if (!window.zE) {
return {
message:
"Sorry, I'm unable to connect to the Support team right now. How else can I assist you?",
};
}
const summary =
params?.user_prompt?.trim() || "User requested human assistance.";
setTimeout(() => {
// Zendesk Messaging: attach the summary as context, then open the widget.
// Create the field first in Zendesk Admin Center → Objects and rules →
// Conversation fields, and put its ID below.
try {
window.zE("messenger:set", "conversationFields", [
{ id: "YOUR_SUMMARY_FIELD_ID", value: summary },
]);
} catch (e) {}
window.zE("messenger", "open");
// Zendesk Web Widget (Classic) — use this instead if you run the
// classic widget rather than Messaging:
// window.zE("webWidget", "open");
agent.hide(); // let Zendesk take over
}, HANDOFF_DELAY_MS);
return {
message: "Transferring you to our Support team now. One moment please.",
};
} catch (error) {
return {
message:
"Something went wrong while connecting to support. Please try again or reach out directly.",
};
}
},
},
});
agent.show();
});
```
Messaging vs Web Widget Classic
The escalation action and trigger are identical across Zendesk products — only the widget call changes. **Zendesk Messaging** carries structured context through pre-configured **conversation fields** (created in Admin Center); the **Web Widget (Classic)** uses `window.zE("webWidget", "open")` and prefill. Confirm which product you run and keep the matching call.
## Email handoff (opens a Zendesk ticket)
[Section titled “Email handoff (opens a Zendesk ticket)”](#email-handoff-opens-a-zendesk-ticket)
When you don’t staff live chat, the Agent can hand off by **email** instead. This is not a real-time transfer — the Agent sends the conversation summary and context to your Zendesk support address, and Zendesk turns that email into a **ticket** for an agent to pick up.
Configure it with the built-in handoff destination, no custom code required:
1. Enable handoff in **Style & Behavior → Data Access & Handoff** ([Style & Behavior](/user-guides/building-your-agent/)).
2. Set the destination to **Email** in [Integrations](/user-guides/integrations/).
3. On escalation, Foldspace emails the conversation summary and captured context to that address.
### The address must be your Zendesk support address
[Section titled “The address must be your Zendesk support address”](#the-address-must-be-your-zendesk-support-address)
This is the rule that makes the difference between a tracked ticket and a lost email:
* **Send to the mailbox Zendesk turns into tickets** — the Zendesk-generated support address, which looks like `support@[your-subdomain].zendesk.com`. (A custom or external support address works too, as long as it’s added under **Admin Center → Channels → Talk and email → Email → Manage support addresses**.)
* **Do not send to a plain human inbox** (e.g. a personal or shared mailbox that isn’t wired into Zendesk). If you do, the handoff email lands in someone’s inbox but Zendesk never creates a ticket — so it isn’t triaged, routed, assigned, or SLA-tracked, and it never appears in the Zendesk Agent Workspace.
In short: point the handoff email at Zendesk’s support address so every escalation becomes a ticket.
## Best practices
[Section titled “Best practices”](#best-practices)
* **User expectations:** the delay before the widget opens (`HANDOFF_DELAY_MS`) gives the user time to read the Agent’s confirmation before the UI switches.
* **Fallback:** if the Zendesk script fails to load (e.g. an ad-blocker), the handler returns a graceful message so the user isn’t left in a dead end.
* **Context is king:** the `user_prompt` summary — or the summary in the ticket email — means your support team starts with the problem already in hand.
* **Pick one path per surface:** use live chat where you staff it and email everywhere else, so users are never routed to a channel no one is watching.
## Related
[Section titled “Related”](#related)
* [Connecting Foldspace to Intercom](/guides/intercom-handoff/) — the same live-handoff pattern for Intercom.
* [Integrations](/user-guides/integrations/) — where handoff destinations (email, HubSpot, webhook) are configured.
* [Style & Behavior](/user-guides/building-your-agent/) — enable handoff and set its escalation conditions.
## Zendesk references
[Section titled “Zendesk references”](#zendesk-references)
Official Zendesk documentation for the APIs and setup this guide relies on:
* [Core messaging Web Widget API](https://developer.zendesk.com/api-reference/widget-messaging/web/core/) — `zE("messenger", "open")` and setting `conversationFields` / `conversationTags`.
* [Web Widget (Classic) API](https://developer.zendesk.com/api-reference/widget/introduction/) — `zE("webWidget", ...)` commands, if you run the classic widget.
* [Adding support email addresses for users to submit tickets](https://support.zendesk.com/hc/en-us/articles/4408842868506-Adding-support-email-addresses-for-users-to-submit-tickets) — create the `…@[subdomain].zendesk.com` address that turns email into tickets.
* [Managing your support addresses](https://support.zendesk.com/hc/en-us/articles/5279521301914-Managing-your-support-addresses) — add, verify, and route custom or external support addresses.
# Compliance
> Foldspace meets enterprise-grade compliance standards — ISO 27001, SOC 2, and GDPR.
Foldspace is built for **enterprise-grade compliance**. Our security and data-handling practices are independently audited against the standards enterprises require.
## Certifications & standards
[Section titled “Certifications & standards”](#certifications--standards)

| Standard | What it covers |
| :------------ | :------------------------------------------------------------------------------------------------------------- |
| **ISO 27001** | Certified information security management — the controls governing how we protect information. |
| **SOC 2** | AICPA Service Organization Control reporting on security, availability, and confidentiality (formerly SAS 70). |
| **GDPR** | Compliance with the EU General Data Protection Regulation for handling personal data. |
## Data residency
[Section titled “Data residency”](#data-residency)
Foldspace operates data centers in both the **United States** and the **European Union**, so you can keep customer data in the region your organization requires.
## Related
[Section titled “Related”](#related)
* [Data processing agreement](/privacy/dpa/): the terms under which Foldspace processes your data.
* [Data privacy policy](/privacy/data-privacy/): how we collect, use, and protect personal data.
* [PII Control](/privacy/pii-masking/): mask personally identifiable information per agent.
For security documentation or to request our reports, contact .
## 🇪🇺 EU support
[Section titled “🇪🇺 EU support”](#-eu-support)
Foldspace fully supports EU deployments:
* **Local EU data center** — data stays within the European Union.
* **EU-supported models** — run on EU-hosted models from Google Gemini and OpenAI.
# Content policy enforcement
> Automatically analyze, flag, and mask content that violates safety policies in agent conversations.
Foldspace can automatically analyze and flag user-generated content across conversations, inputs, and output streams.
When content violates or risks violating a policy, the system attaches a policy flag and masks the relevant content in the conversation to prevent unsafe or sensitive material from being displayed.
Enterprise Feature
Content policy enforcement is available for enterprise customers. To enable or configure moderation and context-sensitive policy handling, contact .
## Supported Policy Categories
[Section titled “Supported Policy Categories”](#supported-policy-categories)
| Policy Name | Description |
| :-------------------- | :----------------------------------------------------------------------------------------------- |
| Dangerous Content | Content that facilitates, promotes, or enables access to harmful goods, services, or activities. |
| Harassment | Content that is malicious, intimidating, bullying, or abusive towards others. |
| Sexually Explicit | Content that is sexually explicit in nature. |
| Hate Speech | Content that is generally accepted as being hate speech. |
| Medical Information | Content that promotes, facilitates, or enables access to harmful medical advice or guidance. |
| Violence & Gore | Content that includes gratuitous or realistic descriptions of violence and/or gore. |
| Obscenity & Profanity | Content that contains vulgar, profane, or offensive language. |
## How It Works
[Section titled “How It Works”](#how-it-works)
Foldspace scans conversational and generated content in real time. If a message matches one or more of the policies above:
1. The system **flags** the violation for moderation and observability.
2. The affected text is **masked** in the conversation to maintain a safe, compliant user experience.
This ensures AI-driven interactions remain aligned with safety standards and organizational policies.
# Cookies notice
> How Foldspace uses cookies and similar technologies, and how to control them.
We use cookies and similar files or technologies on our Website (“Website”) to automatically collect and store information about your computer, device, and Website usage, in order to improve performance and enhance your user experience. In this notice, we use the general term “cookies” to refer to these and all similar technologies that collect information automatically when you use our Website where this notice is posted. You can find out more about cookies and how to control them in the information below.
If you do not accept the use of these cookies, please disable them using the instructions in this Cookies Notice or by changing your browser settings so that cookies from this Website cannot be placed on your computer or mobile device. Important: disabling cookies on this Website may impair certain features.
In this Cookies Notice (“Cookies Notice”), we use the term Foldspace (and “we”, “us”, and “our”) to refer to Foldspace.ai inc. Our Privacy Policy is available [here](/privacy/data-privacy/).
## What is a cookie?
[Section titled “What is a cookie?”](#what-is-a-cookie)
Cookies are computer files containing small amounts of information that are downloaded to your computer or mobile device when you visit a website. Cookies can then be sent back to the originating website on each subsequent visit, or to another website that recognizes that cookie. Cookies are widely used to make websites work, or to work more efficiently, as well as to provide information to the owners of the website.
Cookies perform many different functions, such as letting you navigate between pages efficiently, remembering your preferences, and generally improving the user experience. Cookies may tell us, for example, whether you have visited our Website before or whether you are a new visitor.
There are two broad categories of cookies:
* **First party cookies**, served directly by us to your computer or mobile device.
* **Third party cookies**, which are served by a third party on our behalf. We use third party cookies for functionality, performance/analytics, marketing, and other technologies.
Cookies can remain on your computer or mobile device for different periods of time. Some cookies are ‘session cookies’, meaning that they exist only while your browser is open. These are deleted automatically once you close your browser. Other cookies are ‘persistent cookies’, meaning that they survive after your browser is closed. They can be used by websites to recognize your computer when you open your browser and browse the Internet again.
**Web beacons.** Cookies are not the only way to recognize or track visitors to a website. We may use other, similar technologies from time to time, such as web beacons (sometimes called “tracking pixels” or “clear gifs”). These are small graphics files that contain a unique identifier that enables us to recognize when someone has visited our Website. A tracking pixel is a hyperlink to a resource, usually an image file, embedded into a piece of content like a website or an email. This pixel usually serves no purpose related to the content itself; its sole purpose is to establish a communication by Foldspace to the host of the pixel.
## How do we use cookies?
[Section titled “How do we use cookies?”](#how-do-we-use-cookies)
We use cookies to:
* Track traffic flow and patterns of travel in connection with our Website;
* Understand the total number of visitors to our Website on an ongoing basis and the types of internet browsers (e.g., Chrome, Firefox, Safari, or Internet Explorer) and operating systems (e.g., Windows or Mac) used by our visitors;
* Monitor the performance of our Website and continually improve it; and
* Customize and enhance your online experience.
## What types of cookies do we use?
[Section titled “What types of cookies do we use?”](#what-types-of-cookies-do-we-use)
The types of cookies used by us in connection with the Website can be considered ‘essential Website cookies’, ‘functionality cookies’, ‘analytics and performance cookies’, ‘marketing cookies’, and ‘other technologies’. We’ve set out some further information below, as well as the purposes of the cookies we set, in the following table.
### 1. Cookies necessary for essential Website purposes
[Section titled “1. Cookies necessary for essential Website purposes”](#1-cookies-necessary-for-essential-website-purposes)
These cookies are essential to provide you with services available through this Website and to use some of its features, such as access to secure areas. Without these cookies, we will not be able to provide services that you require, such as transactional pages and secure login accounts.
| Cookie name | Source | Expiry | Purpose |
| :-------------------------------- | :------------------------- | :--------- | :------------------------------------------------------------------------------------------------- |
| cookieyes-consent | First-party (CookieYes) | 1 year | Stores the user’s cookie consent preferences |
| wp\_consent\_preferences | First-party (WordPress) | 1 month | Saves consent choices for cookie categories |
| wp\_consent\_statistics | First-party (WordPress) | 1 month | Records consent choice for analytics cookies |
| wp\_consent\_statistics-anonymous | First-party (WordPress) | 1 month | Records anonymous analytics consent |
| wp\_consent\_functional | First-party (WordPress) | 1 month | Records consent choice for functional cookies |
| wp\_consent\_marketing | First-party (WordPress) | 1 month | Records consent choice for marketing cookies |
| \_\_cf\_bm | First-party (Cloudflare) | 30 minutes | Helps manage incoming traffic and identify bot activity to protect the site |
| \_cfuvid | First-party (Cloudflare) | Session | Used by Cloudflare to identify individual clients behind a shared IP address for security purposes |
| cf\_clearance | First-party (Cloudflare) | 1 year | Stores proof that a challenge (like a CAPTCHA) was successfully completed to allow access |
| \_eucid | First-party (foldspace.ai) | Session | Internal cookie (verify exact purpose/expiry from DevTools; appears before consent) |
### 2. Functionality Cookies
[Section titled “2. Functionality Cookies”](#2-functionality-cookies)
These cookies record information about choices you’ve made and allow us to tailor the Website to you. These cookies allow us to provide you with our services in the way you have requested, as you continue to use or return to our Website. For example, these cookies allow us to:
* Save your location preference if you have set your location on the homepage in order to receive localized information;
* Remember settings you have applied, such as layout, text size, preferences, and colors;
* Show you when you are logged in; and
* Store accessibility options.
| Cookie name | Source | Expiry | Purpose |
| :---------------------- | :-------------------------------- | :--------- | :------------------------------------------------------ |
| wpEmojiSettingsSupports | First-party (WordPress) | Session | Determines whether the browser supports emojis |
| elementor | First-party (Elementor/WordPress) | Persistent | Enables real-time content editing and layout management |
### 3. Performance / Analytics Cookies
[Section titled “3. Performance / Analytics Cookies”](#3-performance--analytics-cookies)
We use performance/analytics cookies to analyze how the Website is accessed, used, or is performing. We do this in order to provide you with a better user experience and to maintain, operate, and continually improve the Website. For example, these cookies allow us to:
* Better understand our Website visitors so that we can improve how we present our content;
* Test different design ideas for particular pages, such as our homepage;
* Collect information about Website visitors, such as where they are located and what browsers they are using;
* Determine the number of unique users of the Website;
* Improve the Website by measuring any errors that occur; and
* Conduct research and diagnostics to improve product offerings.
| Cookie name | Source | Expiry | Purpose |
| :---------- | :-------------------------------------------- | :---------- | :------------------------------------------------- |
| \_\_hssc | Third-party (HubSpot) | 1 hour | Tracks sessions and timestamps for analytics |
| \_\_hssrc | Third-party (HubSpot) | Session | Identifies new browser sessions |
| hubspotutk | First-party (set via HubSpot on foldspace.ai) | 6–12 months | Tracks visitor identity for analytics |
| FPID | Third-party (HubSpot) | 13 months | Stores a unique user identifier for analytics |
| IR\_PI | Third-party (HubSpot) | 13 months | Supports HubSpot performance measurement |
| \_ga | First-party (Google Analytics) | 2 years | Used by Google Analytics to distinguish users |
| *ga*\* | First-party (Google Analytics) | 13 months | Used by Google Analytics to maintain session state |
### 4. Marketing
[Section titled “4. Marketing”](#4-marketing)
We use marketing cookies to deliver many types of targeted digital marketing. We do this in order to provide you with a better user experience and to maintain, operate, and continually improve the Website. The cookies store user information and behavior information (for examples, retargeting visitors who viewed specific pages, measuring the effectiveness of marketing campaigns and excluding existing leads from certain ads), which allows advertising services to target audiences according to variables.
| Cookie name | Source | Expiry | Purpose |
| :----------------- | :----------------------- | :------------------------ | :------------------------------------------------------------------------ |
| li\_gc | Third-party (LinkedIn) | 6 months | Stores user consent for LinkedIn cookies |
| bcookie | Third-party (LinkedIn) | 1 year (confirm actual) | Browser identifier used by LinkedIn |
| li\_sugr | Third-party (LinkedIn) | 3 months (confirm actual) | Used by LinkedIn to store and track identity |
| lidc | Third-party (LinkedIn) | 1 day (confirm actual) | Used by LinkedIn for routing / data center selection |
| lms\_ads | Third-party (LinkedIn) | 1 month (confirm actual) | Used to identify LinkedIn users for advertising |
| lms\_analytics | Third-party (LinkedIn) | 1 month (confirm actual) | Used to identify LinkedIn users for analytics |
| \_gcl\_au | First-party (Google Ads) | 3 months | Used by Google Ads to store and track conversions/advertising performance |
| checkForPermission | Third-party (Beeswax) | 10 minutes | Determines whether cookie consent was accepted |
## How to control or delete cookies
[Section titled “How to control or delete cookies”](#how-to-control-or-delete-cookies)
You may have the right to decide whether to accept or reject cookies. When you access our Website, you are presented with a cookie consent mechanism that allows you to accept or reject cookies that are not essential. You may also control cookies by setting your browser to turn off cookies as described further below. If you turn off cookies, web beacons and other technologies will still detect your visits to our Website; however, they will not be associated with information otherwise stored in cookies.
As another way of controlling cookies, most browsers allow you to change your cookie settings. These settings will typically be found in the “options” or “preferences” menu of your browser. In order to understand these settings and learn how to use them, please consult the “Help” function of your browser, or the documentation published online for your particular browser type and version. However, please note that if you choose to refuse cookies, you may not be able to use the full functionality of our Website. The following pages have information on how to change your cookie settings for different browsers:
* [Cookie settings in Chrome and Chrome mobile](https://support.google.com/chrome/answer/95647)
* [Cookie settings in Firefox and Firefox mobile](https://support.mozilla.org/en-US/kb/cookies-information-websites-store-on-your-computer)
* [Cookie settings in Internet Explorer and Microsoft Edge](https://support.microsoft.com/en-us/help/17442/windows-internet-explorer-delete-manage-cookies)
* [Cookie settings in Safari and Safari mobile](https://support.apple.com/guide/safari/manage-cookies-and-website-data-sfri11471/mac)
* [Cookie settings in iOS](https://support.apple.com/en-us/HT201265)
* [Cookie settings in Opera](https://help.opera.com/en/latest/web-preferences/#cookies)
* [Cookie settings in Apple Safari](https://support.apple.com/guide/safari/manage-cookies-and-website-data-sfri11471/mac)
If you use another browser, you can check if the procedure for your browser is mentioned on [this explanatory website](https://www.allaboutcookies.org/) or [here](https://www.cookiesandyou.com/).
To opt out of being tracked by Google Analytics across all websites, visit [here](https://tools.google.com/dlpage/gaoptout).
## Third Party Websites’ Cookies
[Section titled “Third Party Websites’ Cookies”](#third-party-websites-cookies)
When using our Website, you may be directed to other websites. These websites may use their own cookies. We do not have control over the placement of cookies by other websites you visit, even if you are directed to them from our Website.
If you use the buttons that allow you to share products and content with your friends via social networks like Facebook, X, LinkedIn, or Google, these companies may set a cookie on your device. Find out more about these here:
*
*
*
*
Need More Information? If you would like to find out more about cookies and their use on the Internet, you may find the following link useful: [All About Cookies](https://www.allaboutcookies.org/).
**Contact us.** If you have any questions or comments about this Cookies Notice, or privacy matters generally, please contact us via email at .
# Data privacy policy
> How Foldspace collects, processes, stores, and protects personal data in compliance with global privacy and security standards.
## PURPOSE
[Section titled “PURPOSE”](#purpose)
Foldspace.ai inc. (herein referred to as “Organization,” “Company,” “we,” “our,” or “us,” etc.) is committed to protecting the privacy of individuals who interact with us. This policy explains how we collect, use, store, and safeguard personal data to ensure transparency and build trust with our users, customers, and partners.
## SCOPE
[Section titled “SCOPE”](#scope)
This policy applies to all personal data collected through the organization’s websites, applications, services, and other interactions with individuals.
## DEFINITION
[Section titled “DEFINITION”](#definition)
* **IP Address:** A unique string of characters that identifies each computer using the Internet Protocol to communicate over a network.
* **Personal Data:** Any information relating to an identifiable individual (e.g., name, email, IP address).
* **Processing:** Any operation performed on personal data, such as collection, storage, use, disclosure, or deletion.
* **Data Controller:** The entity that determines the purposes and means of processing personal data.
* **Data Processor:** The entity that processes data on behalf of the data controller.
## RESPONSIBILITIES
[Section titled “RESPONSIBILITIES”](#responsibilities)
* The Head of Technology is responsible for developing, implementing, maintaining, and enforcing the policy.
* Employees are responsible and/or accountable to ensure adherence to this policy’s terms during their job duties.
## POLICY
[Section titled “POLICY”](#policy)
### Data We Collect
[Section titled “Data We Collect”](#data-we-collect)
We may collect the following types of personal data:
* **Identification Information:** Name, email, phone number, address, date of birth, etc.
* **Account Information:** Username, account preferences.
* **Technical Data:** IP address, device identifiers, operating system, cookies, and usage analytics.
### How We Collect Data
[Section titled “How We Collect Data”](#how-we-collect-data)
We collect personal data through the following methods:
* **Directly from you:** When you fill out forms, create accounts, or contact us.
* **Automatically:** Through cookies.
* **Third Parties:** From business partners, service providers, or publicly available sources.
### Legal Bases for Processing Personal Data
[Section titled “Legal Bases for Processing Personal Data”](#legal-bases-for-processing-personal-data)
We process personal data only when permitted by law. The legal bases include:
* **Consent:** When you provide explicit consent (e.g., marketing communications).
* **Contractual Necessity:** To fulfill a contract with you (e.g., processing orders).
* **Legal Obligation:** To comply with legal and regulatory requirements.
* **Legitimate Interests:** For fraud prevention, improving services, or ensuring security.
### How We Use Personal Data
[Section titled “How We Use Personal Data”](#how-we-use-personal-data)
We use personal data for the following purposes:
* Providing and improving our services.
* Processing transactions and managing accounts.
* Communicating with you regarding updates, offers, and support.
* Conducting analytics and research to improve user experience.
* Ensure security, detect fraud, and comply with legal obligations.
### Data Sharing and Disclosure
[Section titled “Data Sharing and Disclosure”](#data-sharing-and-disclosure)
We may share personal data under these circumstances:
* **Service Providers:** With vendors or contractors who perform services on our behalf.
* **Legal Compliance:** To comply with laws, subpoenas, or other legal processes.
* **Business Transfers:** In the event of mergers, acquisitions, or asset sales.
* **Consent:** When you explicitly agree to share your data.
## DATA RETENTION
[Section titled “DATA RETENTION”](#data-retention)
We retain personal data only as long as necessary to fulfill the purposes for which it was collected, unless a more extended retention period is required by law. Afterward, data is securely deleted or anonymized.
### Retention Periods for Different Data Types
[Section titled “Retention Periods for Different Data Types”](#retention-periods-for-different-data-types)
* **User Account Data:** Retained for the duration of your use of Foldspace.ai inc. services. Once your account is closed, data will be securely deleted or anonymized within 90 days, unless otherwise required by legal obligations.
* **Usage Data:** Includes information about your interactions with Foldspace.ai inc. platform, such as feature usage, onboarding quizzes, session replays, and product engagement metrics. This data is retained for the duration of your use of Foldspace.ai inc. services to provide personalized experiences, improve platform functionality, and support service optimization. Once your account is closed, usage data is securely deleted or anonymized within 90 days.
* **Backup Data:** Retained for up to 30 days in secure backups, after which it is deleted or overwritten as part of routine maintenance.
### Secure Data Deletion
[Section titled “Secure Data Deletion”](#secure-data-deletion)
When data is no longer needed for the purposes for which it was collected or required by law, it is securely deleted using industry-standard methods to ensure it cannot be recovered. This includes:
* Secure overwriting for electronic data.
* Secure destruction for physical records, if applicable.
### Data Security
[Section titled “Data Security”](#data-security)
We implement industry-standard technical and organizational measures to safeguard personal data, including:
* Encryption (at rest and in transit).
* Access controls and authentication protocols.
* Regular audits and security assessments.
### Your Rights
[Section titled “Your Rights”](#your-rights)
Depending on your jurisdiction, you may have the following rights:
* **Access:** Request access to your data.
* **Rectification:** Correct inaccurate or incomplete data.
* **Deletion:** Request deletion of your data.
* **Data Portability:** Obtain a copy of your data in a portable format.
* **Restriction:** Request a limitation on the processing of your data.
* **Objection:** Object to certain processing activities (e.g., direct marketing).
* **Withdraw Consent:** Withdraw your consent when processing is based on consent.
To exercise your rights, please contact us at .
## Where We Store and Process Personal Data
[Section titled “Where We Store and Process Personal Data”](#where-we-store-and-process-personal-data)
Foldspace.ai inc. operates on Google Cloud Platform (GCP), utilizing its secure and reliable infrastructure located in the United States. GCP provides a robust and scalable environment that enables us to deliver our services efficiently while maintaining strong data protection measures. You can learn more about GCP at [cloud.google.com](https://cloud.google.com).
### Data Storage and Processing
[Section titled “Data Storage and Processing”](#data-storage-and-processing)
Currently, all personal data collected by Foldspace.ai inc. is stored and processed exclusively in GCP data centers located in the United States. By centralizing data within the US, we ensure operational efficiency and compliance with applicable laws governing data storage and processing in this region.
### Addendum: EU Data Center
[Section titled “Addendum: EU Data Center”](#addendum-eu-data-center)
An EU data center is available for customers who require a fully local deployment in the European Union. For these deployments, personal data is stored and processed entirely within the EU. The US and EU data centers are completely separated, and data is not shared or replicated between the two regions.
### Compliance and Security
[Section titled “Compliance and Security”](#compliance-and-security)
Foldspace.ai inc. leverages GCP’s advanced compliance programs and certifications to ensure that your personal data is stored and processed securely. GCP is independently validated against a range of international and industry-specific standards, including:
* **ISO/IEC 27001:** Information Security Management
* **ISO/IEC 27018:** Protection of Personally Identifiable Information
* **SOC 1, SOC 2, and SOC 3:** Service Organization Controls
* **GDPR Compliance:** Ensuring compliance with the General Data Protection Regulation where applicable.
### International Transfers
[Section titled “International Transfers”](#international-transfers)
For users located outside the United States, the transfer of personal data to the US is managed in accordance with applicable laws and safeguards. While Foldspace.ai inc. currently uses GCP data centers exclusively in the US, we are committed to implementing measures such as encryption, pseudonymization, and compliance with data transfer frameworks to protect your data.
For questions about how your data is stored, processed, or transferred, please contact us at .
## SECURITY
[Section titled “SECURITY”](#security)
We take reasonable measures to protect your personal information from unauthorized access, loss, or misuse. These measures include implementing industry-standard encryption, access controls, and regular security audits to safeguard your data.
However, no method of transmission over the Internet or electronic storage is entirely secure. In the unlikely event of a data breach, we have a detailed Data Breach Procedure in place to address such incidents swiftly and effectively, ensuring transparency and mitigation of potential risks to affected parties.
## International Transfers and the EU-US Data Privacy Framework
[Section titled “International Transfers and the EU-US Data Privacy Framework”](#international-transfers-and-the-eu-us-data-privacy-framework)
With the implementation of the EU-US Data Privacy Framework as of July 10, 2023, Foldspace.ai inc. ensures that personal data transfers between the European Union (EU) and the United States (US) are conducted in compliance with this framework. This means personal data transfers can now be performed without the need for additional Supplementary Measures, provided the framework’s provisions are met.
Foldspace.ai inc. adheres to the requirements of the EU-US Data Privacy Framework to safeguard your data during cross-border transfers. This includes implementing measures that align with the framework’s principles of accountability, transparency, and data protection.
### Processing Activities with Foldspace.ai inc. and Sub-Processors
[Section titled “Processing Activities with Foldspace.ai inc. and Sub-Processors”](#processing-activities-with-foldspaceai-inc-and-sub-processors)
Processing activities conducted by Foldspace.ai inc. and its sub-processors remain unchanged; however, we have updated the tools and legal frameworks we rely on for international data transfers. Foldspace.ai inc. has decided to maintain Supplementary Measures (such as encryption and pseudonymization) to ensure continued data protection. Additionally, we rely on the EU-US Data Privacy Framework (DPF) as an added layer of security for transfers of personal data between the EU and the US.
### Sub-Processors Participating in the Data Privacy Framework
[Section titled “Sub-Processors Participating in the Data Privacy Framework”](#sub-processors-participating-in-the-data-privacy-framework)
The following Foldspace.ai inc. sub-processors are currently active participants in the EU-US Data Privacy Framework, and we will continue to review and update this list as necessary to ensure full compliance:
* Google Cloud Platform (GCP)
* Gemini
* OpenAI
* SendGrid (Twilio)
* Google Analytics
* Hubspot
### Transfers to Sub-Processors Not in the DPF
[Section titled “Transfers to Sub-Processors Not in the DPF”](#transfers-to-sub-processors-not-in-the-dpf)
For US-based entities not listed in the Data Privacy Framework List, transfers cannot rely solely on the Adequacy Decision provided by the DPF. In such cases, Foldspace.ai inc. ensures that these transfers are supported by appropriate safeguards, as required under Article 46 of the GDPR. These safeguards include:
* Standard Data Protection Clauses (SCCs)
* Binding Corporate Rules (BCRs)
* Additional technical and organizational measures, such as encryption and contractual obligations, to protect data subjects’ rights
### Continuous Monitoring and Compliance
[Section titled “Continuous Monitoring and Compliance”](#continuous-monitoring-and-compliance)
Foldspace.ai inc. is committed to ensuring that all sub-processors comply with the necessary data protection standards. We actively monitor the Data Privacy Framework List and conduct regular reviews of our sub-processors to ensure alignment with evolving regulatory requirements. For more information on how Foldspace.ai inc. ensures compliance with the EU-US Data Privacy Framework and manages international data transfers, contact us at .
## Automated Decision-Making and Profiling
[Section titled “Automated Decision-Making and Profiling”](#automated-decision-making-and-profiling)
Foldspace.ai inc. utilizes AI-powered tools, such as Foldspace.ai inc. AI agent, to enhance user experiences and streamline platform functionality. In certain cases, automated decision-making and profiling may be employed as part of our services.
### What Automated Decision-Making and Profiling Entail
[Section titled “What Automated Decision-Making and Profiling Entail”](#what-automated-decision-making-and-profiling-entail)
* Automated decision-making involves decisions made by AI systems without human intervention.
* Profiling involves analyzing personal data to evaluate certain aspects of a user, such as preferences, behavior, or usage patterns.
Examples of automated processes in Foldspace.ai inc. platform include:
* Personalized Recommendations
* Usage Analytics
* Subscription Management
### User Rights Regarding Automated Decision-Making and Profiling
[Section titled “User Rights Regarding Automated Decision-Making and Profiling”](#user-rights-regarding-automated-decision-making-and-profiling)
As per the GDPR, users have specific rights:
* Right to Object
* Right to Explanation
## Cookies and Tracking Technologies
[Section titled “Cookies and Tracking Technologies”](#cookies-and-tracking-technologies)
We use cookies to enhance your experience. Types of cookies include:
* Essential Cookies
* Analytics Cookies (e.g., \_eucid)
### Consent for EU Users
[Section titled “Consent for EU Users”](#consent-for-eu-users)
* Essential cookies do not require consent.
* Manage preferences via browser settings.
For more details, see our [Cookies Notice](/privacy/cookies/).
## Children’s Privacy
[Section titled “Children’s Privacy”](#childrens-privacy)
Not intended for users under 13. If data was collected, contact us for deletion.
## Third-Party Links
[Section titled “Third-Party Links”](#third-party-links)
We are not responsible for external privacy practices.
## Updates to This Policy
[Section titled “Updates to This Policy”](#updates-to-this-policy)
We may update this policy as laws or practices evolve.
## Data Protection Officer (DPO)
[Section titled “Data Protection Officer (DPO)”](#data-protection-officer-dpo)
We have appointed a Data Protection Officer (DPO) responsible for overseeing data protection and privacy compliance.
For any privacy-related queries, data subject requests, or concerns regarding personal data processing, you may contact our DPO at:
**Email:**
The DPO monitors compliance with applicable data protection regulations and acts as the point of contact for users and supervisory authorities.
# Data processing agreement
> Legal framework for how Foldspace processes personal data on behalf of customers.
This Data Processing Agreement (“DPA”) is made and entered into as of this \_\_\_\_ day of \_\_\_\_, 202\_ forms part of the Foldspace Agreement (the “Agreement”). You acknowledge that you, on behalf of \[\_] incorporated under \_\_\_\_\_\_ law, with its principal offices located at \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ (“Organization”) (collectively, “You”, “Your”, “Customer”, or “Data Controller”) have read and understood and agree to comply with this DPA, and are entering into a binding legal agreement with Foldspace Inc,. (“Foldspace”, “Us”, “We”, “Our”, “Service Provider” or “Data Processor”) to reflect the parties’ agreement with regard to the Processing of Personal Data (as such terms are defined below). Both parties shall be referred to as the “Parties” and each, a “Party”.
WHEREAS, Foldspace shall provide the services set forth in the Agreement (collectively, the “Services”) for Customer, as described in the Agreement; and
WHEREAS, In the course of providing the Services pursuant to the Agreement, we may process Personal Data on your behalf, in the capacity of a “Data Processor”; and the Parties wish to set forth the arrangements concerning the processing of Personal Data (defined below) within the context of the Services and agree to comply with the following provisions with respect to any Personal Data, each acting reasonably and in good faith.
NOW THEREFORE, in consideration of the mutual promises set forth herein and other good and valuable consideration, the receipt and sufficiency of which are hereby acknowledged by the Parties, intending to be legally bound, agree as follows:
## INTERPRETATION AND DEFINITIONS
[Section titled “INTERPRETATION AND DEFINITIONS”](#interpretation-and-definitions)
The headings contained in this DPA are for convenience only and shall not be interpreted to limit or otherwise affect the provisions of this DPA. References to clauses or sections are references to the clauses or sections of this DPA unless otherwise stated. Words used in the singular include the plural and vice versa, as the context may require. Capitalized terms not defined herein shall have the meanings assigned to such terms in the Agreement. Definitions:
* **“Affiliate”** means any entity that directly or indirectly controls, is controlled by, or is under common control with the subject entity. “Control”, for purposes of this definition, means direct or indirect ownership or control of more than 50% of the voting interests of the subject entity.
* **“Controller” or “Data Controller”** means the entity which determines the purposes and means of the Processing of Personal Data. For the purposes of this DPA only, and except where indicated otherwise, the term “Data Controller” shall include the Organization and/or the Organization’s Authorized Affiliates.
* **“Data Protection Laws and Regulations”** means all laws and regulations of the European Union, the European Economic Area and their Member States, including the GDPR, the UK GDPR, and the Israeli Privacy Protection Law, 5741–1981 and the regulations promulgated thereunder (including Privacy Protection Regulations (Transfer of Data to Databases Abroad), 5761-2001 and Privacy Protection Regulations (Data Security), 5777-2017), and any binding instructions, guidelines and requirements of the Israeli Privacy Protection Authority, as applicable to the Processing of Personal Data under the Agreement.
* **“Data Subject”** means the identified or identifiable person to whom the Personal Data relates.
* **“Foldspace Group”** means Foldspace Inc., Andrena Ltd., and its Affiliates, and their employees, personnel, contractors and consultants engaged in the Processing of Personal Data.
* **“Member State”** means a country that belongs to the European Union and/or the European Economic Area. “Union” means the European Union.
* **“GDPR”** means the Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016 on the protection of natural persons with regard to the processing of personal data and on the free movement of such data, and repealing Directive 95/46/EC (General Data Protection Regulation).
* **“Personal Data” or “Personal Information”** means any information relating to an identified or identifiable natural person; an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person, as defined under Data Protection Laws and Regulations. For the avoidance of doubt, Customer’s business contact information is not by itself deemed to be Personal Data subject to this DPA.
* **“Personnel”** mean an agent, employee, contractors, and/or subcontractor employed or retained in any way, on a full or part time basis, by Foldspace or any of its Affiliates, as well as any employee or agent of a Sub-processor of Foldspace or any of its Affiliates.
* **“Process(ing)”** means any operation or set of operations which is performed upon Personal Data, whether or not by automatic means, such as collection, recording, organization, structuring, storage, adaptation or alteration, retrieval, consultation, use, disclosure by transmission, dissemination or otherwise making available, alignment or combination, restriction, erasure or destruction.
* **“Processor” or “Data Processor”** means the entity which Processes Personal Data on behalf of the Controller.
* **“Security Documentation”** means the Security Documentation applicable to the specific Services purchased by Customer, as updated from time to time. Customer shall send a request to to receive a copy of the Security Documentation.
* **“Standard Contractual Clauses” or “SCCs”** means (i) the standard contractual clauses for the transfer of Personal Data to Data processors established in third countries which do not ensure an adequate level of protection as set out in Regulation (EU) 2016/679 of the European Parliament and of the Council from June 4, 2021, as updated, amended, replaced or superseded from time to time by the European Commission; or (ii) where required from time to time by a supervisory authority for use with respect to any specific restricted transfer, any other set of contractual clauses or other similar mechanism approved by such Supervisory Authority or by Applicable Laws for use in respect of such Restricted Transfer, as updated, amended, replaced or superseded from time to time by such Regulatory Authority or Data Protection Laws and Regulations.
* **“Sub-processor”** means any Processor engaged by Foldspace and/or Foldspace Affiliate to Process Personal Data on behalf of Customer.
* **“Supervisory Authority”** means an independent public authority which is established by an EU Member State pursuant to the GDPR.
* **“UK GDPR”** means the Data Protection Act 2018, as updated, amended, replaced or superseded from time to time by the ICO.
* **“UK Standard Contractual Clauses” or “UK SCCs”** means the standard contractual clauses for the transfer of Personal Data to Data processors established in third countries which do not ensure an adequate level of protection as set out by the ICO, as updated, amended, replaced or superseded from time to time by the ICO.
## PROCESSING OF PERSONAL DATA
[Section titled “PROCESSING OF PERSONAL DATA”](#processing-of-personal-data)
The Parties acknowledge and agree that with regard to the Processing of Personal Data under this DPA Foldspace is the Data Processor and Foldspace or members of the Foldspace Group may engage Sub-processors pursuant to the requirements set forth in Section 5 “Sub-processors” below. For clarity, this DPA shall not apply with respect to Foldspace processing activity as a Data Controller with respect to Foldspace data as detailed in Foldspace’s [privacy policy](/privacy/data-privacy/).
Customer shall, in its use of the Services, Process Personal Data in accordance with the requirements of Data Protection Laws and Regulations and comply at all times with the obligations applicable to data controllers (including, without limitation, Article 24 of the GDPR). For the avoidance of doubt, Customer’s instructions for the Processing of Personal Data shall comply with Data Protection Laws and Regulations. Customer shall have sole responsibility for the means by which Customer acquired Personal Data. Without limitation, Customer shall comply with any and all transparency-related obligations (including, without limitation, displaying any and all relevant and required privacy notices or policies) and shall at all times have any and all required ongoing legal bases in order to collect, Process and transfer to Foldspace the Personal Data and to authorize the Processing by Foldspace of the Personal Data which is authorized in this DPA. Customer shall defend, hold harmless and indemnify Foldspace, its Affiliates and subsidiaries (including without limitation their directors, officers, agents, subcontractors and/or employees) from and against any liability of any kind related to any breach, violation or infringement by Customer and/or its authorized users of any Data Protection Laws and Regulations and/or this DPA and/or this Section.
### Foldspace’s Processing of Personal Data
[Section titled “Foldspace’s Processing of Personal Data”](#foldspaces-processing-of-personal-data)
Subject to the Agreement, Foldspace shall Process Personal Data that is subject to this DPA only in accordance with Customer’s documented instructions as necessary for the performance of the Services and for the performance of the Agreement and this DPA, unless required to otherwise by Union or Member State law or any other applicable law to which Foldspace and its Affiliates are subject, in which case, Foldspace shall inform the Customer of the legal requirement before processing, unless that law prohibits such information on important grounds of public interest. The duration of the Processing, the nature and purposes of the Processing, as well as the types of Personal Data Processed and categories of Data Subjects under this DPA are further specified in Schedule 1 (Details of the Processing) to this DPA.
To the extent that Foldspace or its Affiliates cannot comply with a request (including, without limitation, any instruction, direction, code of conduct, certification, or change of any kind) from Customer and/or its authorized users relating to Processing of Personal Data or where Foldspace considers such a request to be unlawful, Foldspace (i) shall inform Customer, providing relevant details of the problem (but not legal advice), (ii) Foldspace may, without any kind of liability towards Customer, temporarily cease all Processing of the affected Personal Data (other than securely storing those data), and (iii) if the Parties do not agree on a resolution to the issue in question and the costs thereof, each Party may, as its sole remedy, terminate the Agreement and this DPA with respect to the affected Processing, and Customer shall pay to Foldspace all the amounts owed to Foldspace or due before the date of termination. Customer will have no further claims against Foldspace (including, without limitation, requesting refunds for Services) due to the termination of the Agreement and/or the DPA in the situation described in this paragraph (excluding the obligations relating to the termination of this DPA set forth below).
Foldspace will not be liable in the event of any claim brought by a third party, including, without limitation, a Data Subject, arising from any act or omission of Foldspace, to the extent that such is a result of Customer’s instructions.
## RIGHTS OF DATA SUBJECTS
[Section titled “RIGHTS OF DATA SUBJECTS”](#rights-of-data-subjects)
If Foldspace receives a request from a Data Subject to exercise its rights as described under Data Protection Laws and Regulations (“Data Subject Request”), Foldspace shall, to the extent legally permitted, promptly notify and forward such Data Subject Request to Customer. Taking into account the nature of the Processing, Foldspace shall use commercially reasonable efforts to assist Customer by appropriate technical and organizational measures, insofar as this is possible, for the fulfilment of Customer’s obligation to respond to a Data Subject Request under Data Protection Laws and Regulations. To the extent legally permitted, Customer shall be responsible for any costs arising from Foldspace’s provision of such assistance.
## FOLDSPACE PERSONNEL
[Section titled “FOLDSPACE PERSONNEL”](#foldspace-personnel)
Foldspace shall grant access to the Personal Data to its Personnel under its authority only on a need to know basis and ensure that such persons engaged in the Processing of Personal Data have committed themselves to confidentiality or are under an appropriate statutory obligation of confidentiality.
Foldspace may disclose and Process the Personal Data (a) as permitted hereunder (b) to the extent required by a court of competent jurisdiction or other Supervisory Authority and/or otherwise as required by applicable laws or applicable Data Protection Laws and Regulations (in such a case, Foldspace shall inform the Customer of the legal requirement before the disclosure, unless that law prohibits such information on important grounds of public interest), or (c) on a “need-to-know” basis under an obligation of confidentiality to legal counsel(s), data protection advisor(s), accountant(s), investors or potential acquirers.
## AUTHORIZATION REGARDING SUB-PROCESSORS
[Section titled “AUTHORIZATION REGARDING SUB-PROCESSORS”](#authorization-regarding-sub-processors)
Foldspace’s current list of Sub-processors is included in Schedule 2 (“Sub-processor List”) and is hereby approved by Data Controller. Customer hereby grants a general authorization to Foldspace to appoint new Sub-processors, and Foldspace shall comply with the conditions of Section 5.2, to 5.4. The Sub-processor List as of the date of execution of this DPA is hereby authorized by Customer.
Customer shall send an email to with the subject **SUBSCRIPTION TO SUB-PROCESSORS NOTIFICATION**, to subscribe to notifications of new Sub-processors, and if Customer subscribes, Foldspace shall provide notification of any new Sub-processor(s).
Customer may reasonably object to Foldspace’s use of a Sub-processor for reasons related to the GDPR by notifying Foldspace promptly in writing within **three (3) business days** after receipt of Foldspace’s notice in accordance with the mechanism set out in Section 5.2 and such written objection shall include the reasons related to the GDPR for objecting to Foldspace’s use of such Sub-processor. Failure to object to such Sub-processor in writing within three (3) business days following Foldspace’s notice shall be deemed as acceptance of the Sub-Processor. In the event Customer reasonably objects to a Sub-processor, as permitted in the preceding sentences, Foldspace will use reasonable efforts to make available to Customer a change in the Services or recommend a commercially reasonable change to Customer’s use of the Services to avoid Processing of Personal Data by the objected-to Sub-processor without unreasonably burdening the Customer. If Foldspace is unable to make available such change within a reasonable period of time, which shall not exceed thirty (30) days, Customer may, as a sole remedy, terminate the applicable Agreement and this DPA with respect only to those Services which cannot be provided by Foldspace without the use of the objected-to Sub-processor by providing written notice to Foldspace provided that all amounts due under the Agreement before the termination date with respect to the Processing at issue shall be duly paid to Foldspace. Until a decision is made regarding the Sub-processor, Foldspace may temporarily suspend the Processing of the affected Personal Data. Customer will have no further claims against Foldspace due to the termination of the Agreement (including, without limitation, requesting refunds) and/or the DPA in the situation described in this paragraph.
This Section 5 shall not apply to subcontractors of Foldspace which provide ancillary services to support the performance of the DPA. This includes, for example, telecommunication services, maintenance and user service, cleaning staff, or auditors.
## SECURITY
[Section titled “SECURITY”](#security)
Taking into account the state of the art, the costs of implementation, the scope, the context, the purposes of the Processing as well as the risk of varying likelihood and severity for the rights and freedoms of natural persons, Foldspace shall maintain all industry-standard technical and organizational measures for protection of the security (including protection against unauthorized or unlawful Processing and against accidental or unlawful destruction, loss or alteration or damage, unauthorized disclosure of, or access to, Personal Data), confidentiality and integrity of Personal Data, as set forth in the Security Documentation which are hereby approved by Customer. Upon the Customer’s request, Foldspace will use commercially reasonable efforts to assist Customer, at Customer’s cost, in ensuring compliance with the obligations under Data Protection Laws and Regulations, taking into account the nature of the processing, the state of the art, and the information available to Foldspace.
Upon Customer’s written request at reasonable intervals, and subject to the confidentiality obligations set forth in the Agreement and this DPA, Foldspace shall make available to Customer (or Customer’s independent, third-party auditor that is not a competitor of Foldspace) a copy or a summary of Foldspace’s then most recent third-party audits or certifications, as applicable (provided, however, that such audits, certifications and the results therefrom, including the documents reflecting the outcome of the audit and/or the certifications, shall only be used by Customer to assess compliance with this DPA, and shall not be used for any other purpose or disclosed to any third party without Foldspace’s prior written approval and, upon Foldspace’s first request, Customer shall return all records or documentation in Customer’s possession or control provided by Foldspace in the context of the audit and/or the certification). At Customer’s cost and expense, Foldspace shall allow for and contribute to audits, including inspections of Foldspace’s, conducted by the controller or another auditor mandated by the controller (who is not a direct or indirect competitor of Foldspace) provided that the parties shall agree on the scope, methodology, timing and conditions of such audits and inspections. Notwithstanding anything to the contrary, nothing in this DPA will require Foldspace either to disclose to Customer (and/or its authorized auditors), or provide access to: (i) any data of any other customer of Foldspace; (ii) Foldspace’s internal accounting or financial information; (iii) any trade secret of Foldspace; or (iv) any information that, in Foldspace’s sole reasonable discretion, could compromise the security of any of Foldspace’s systems or premises or cause Foldspace to breach obligations under any applicable law or its obligations to any third party.
## PERSONAL DATA INCIDENT MANAGEMENT AND NOTIFICATION
[Section titled “PERSONAL DATA INCIDENT MANAGEMENT AND NOTIFICATION”](#personal-data-incident-management-and-notification)
Foldspace shall notify Customer without undue delay after becoming aware of the accidental or unlawful destruction, loss, alteration, unauthorized disclosure of, or access to Personal Data, including Personal Data, transmitted, stored or otherwise Processed by Foldspace of which Foldspace becomes aware (a “Personal Data Incident”). Foldspace shall make reasonable efforts to identify the cause of such Personal Data Incident and take those steps as Foldspace deems necessary, possible and reasonable in order to remediate the cause of such a Personal Data Incident to the extent the remediation is within Foldspace’s reasonable control. In any event, Customer will be the party responsible for notifying supervisory authorities and/or concerned data subjects (where required by Data Protection Laws and Regulations). Foldspace’s notification of or response to a Personal Data Incident under this Section 7 will not be construed as an acknowledgement by Foldspace of any fault or liability with respect to the Personal Data Incident.
## RETURN AND DELETION OF PERSONAL DATA
[Section titled “RETURN AND DELETION OF PERSONAL DATA”](#return-and-deletion-of-personal-data)
Subject to the Agreement, Foldspace shall, at the choice of Customer, delete or return the Personal Data to Customer after the end of the provision of the Services relating to Processing, and shall delete existing copies unless applicable law requires storage of the Personal Data. In any event, to the extent required or allowed by applicable law, Foldspace may retain one copy of the Personal Data for evidence purposes and/or for the establishment, exercise or defence of legal claims and/or to comply with applicable laws and regulations. If the Customer requests the Personal Data to be returned, the Personal Data shall be returned in the format generally available for Foldspace’s Customers.
## AUTHORIZED AFFILIATES
[Section titled “AUTHORIZED AFFILIATES”](#authorized-affiliates)
The Parties acknowledge and agree that, by executing the DPA, the Customer enters into the DPA on behalf of itself and, as applicable, in the name and on behalf of its Authorized Affiliates, thereby establishing a separate DPA between Foldspace. Each Authorized Affiliate agrees to be bound by the obligations under this DPA. All access to and use of the Services by Authorized Affiliates must comply with the terms and conditions of the Agreement and this DPA and any violation of the terms and conditions therein by an Authorized Affiliate shall be deemed a violation by Customer.
The Customer shall remain responsible for coordinating all communication with Foldspace under the Agreement and this DPA and shall be entitled to make and receive any communication in relation to this DPA on behalf of its Authorized Affiliates.
## TRANSFERS OF DATA
[Section titled “TRANSFERS OF DATA”](#transfers-of-data)
Personal Data may be transferred from the EU Member States, the three EEA member countries (Norway, Liechtenstein and Iceland) (collectively, “EEA”), the United Kingdom to countries that offer adequate level of data protection under or pursuant to the adequacy decisions published by the relevant data protection authorities of the EEA, the Union, the Member States or the European Commission, the UK supervisory authority (“Adequacy Decisions”), without any further safeguard being necessary.
To the extent that there is Processing of Personal Data which includes transfers from the EEA, the UK to countries which do not offer adequate level of data protection or which have not been subject to an Adequacy Decision (“Other Countries”), the below terms shall apply:
With respect to the EU transfers of Personal Data, Customer as a Data Exporter (as defined in the SCCs) and Foldspace on behalf of itself and each Foldspace Affiliate (as applicable) as a Data Importer (as defined in the SCCs) hereby enter into the SCC set out in Schedule 3. To the extent that there is any conflict or inconsistency between the terms of the SCC and the terms of this DPA, the terms of the SCC shall take precedence.
With respect to the UK transfers of Personal Data (from the UK to other countries which have not been subject to a relevant Adequacy Decision), Customer as a Data Exporter (as defined in the UK SCCs) and Foldspace on behalf of itself and each Foldspace Affiliate (as applicable) as a Data Importer (as defined in the UK SCCs), hereby enter into the UK SCC set out in Schedule 3. To the extent that there is any conflict or inconsistency between the terms of the UK SCC and the terms of this DPA, the terms of the UK SCC shall take precedence.
## TERMINATION
[Section titled “TERMINATION”](#termination)
This DPA shall automatically terminate upon the termination or expiration of the Agreement under which the Services are provided. Sections 2.2, 2.3.3, 8 and 12 shall survive the termination or expiration of this DPA for any reason. This DPA cannot, in principle, be terminated separately to the Agreement, except where the Processing ends before the termination of the Agreement, in which case, this DPA shall automatically terminate.
## RELATIONSHIP WITH AGREEMENT
[Section titled “RELATIONSHIP WITH AGREEMENT”](#relationship-with-agreement)
In the event of any conflict between the provisions of this DPA and the provisions of the Agreement, the provisions of this DPA shall prevail over the conflicting provisions of the Agreement. Notwithstanding anything to the contrary in the Agreement and/or in any agreement between the parties and to the maximum extent permitted by law: (A) Foldspace’s (including Foldspace’s Affiliates’) entire, total and aggregate liability, related to personal data or information, privacy, or for breach of, this DPA and/or Data Protection Laws and Regulations, including, without limitation, if any, any indemnification obligation or applicable law regarding data protection or privacy, shall be limited to the amounts paid to Foldspace under the Agreement within twelve (12) months preceding the event that gave rise to the claim. This limitation of liability is cumulative and not per incident; (B) In no event will Foldspace and/or Foldspace Affiliates and/or their third-party providers, be liable under, or otherwise in connection with this DPA for: (i) any indirect, exemplary, special, consequential, incidental or punitive damages; (ii) any loss of profits, business, or anticipated savings; (iii) any loss of, or damage to data, reputation, revenue or goodwill; and/or (iv) the cost of procuring any substitute goods or services; and (C) The foregoing exclusions and limitations on liability set forth in this Section shall apply: (i) even if Foldspace, Foldspace Affiliates or third-party providers, have been advised, or should have been aware, of the possibility of losses or damages; (ii) even if any remedy in this DPA fails of its essential purpose; and (iii) regardless of the form, theory or basis of liability (such as, but not limited to, breach of contract or tort).
## AMENDMENTS
[Section titled “AMENDMENTS”](#amendments)
This DPA may be amended at any time by a written instrument duly signed by each of the Parties.
## LEGAL EFFECT
[Section titled “LEGAL EFFECT”](#legal-effect)
This DPA shall only become legally binding between Customer and Foldspace when the formal steps set out in the Section “INSTRUCTIONS ON HOW TO EXECUTE THIS DPA” below have been fully completed. Foldspace may assign this DPA or its rights or obligations hereunder to any Affiliate thereof, or to a successor or any Affiliate thereof, in connection with a merger, consolidation or acquisition of all or substantially all of its shares, assets or business relating to this DPA or the Agreement. Any Foldspace obligation hereunder may be performed (in whole or in part), and any Foldspace right (including invoice and payment rights) or remedy may be exercised (in whole or in part), by an Affiliate of Foldspace.
## SIGNATURE
[Section titled “SIGNATURE”](#signature)
The Parties represent and warrant that they each have the power to enter into, execute, perform and be bound by this DPA. You, as the signing person on behalf of Customer, represent and warrant that you have, or you were granted, full authority to bind the Organization and, as applicable, its Authorized Affiliates to this DPA. If you cannot, or do not have authority to, bind the Organization and/or its Authorized Affiliates, you shall not supply or provide Personal Data to Foldspace. By signing this DPA, Customer enters into this DPA on behalf of itself and, to the extent required or permitted under applicable Data Protection Laws and Regulations, in the name and on behalf of its Authorized Affiliates, if and to the extent that Foldspace processes Personal Data for which such Authorized Affiliates qualify as the/a “data controller”.
This DPA has been pre-signed on behalf of Foldspace.
### Instructions on how to execute this DPA
[Section titled “Instructions on how to execute this DPA”](#instructions-on-how-to-execute-this-dpa)
1. Complete the missing information; and
2. Send the completed and signed DPA to us by email, indicating the Customer’s name, to .
***
## SCHEDULE 1 - DETAILS OF THE PROCESSING
[Section titled “SCHEDULE 1 - DETAILS OF THE PROCESSING”](#schedule-1---details-of-the-processing)
**Subject matter.** Foldspace will Process Personal Data as necessary to perform the Services pursuant to the Agreement, as further instructed by Customer in its use of the Services.
**Nature and Purpose of Processing.**
* Performing the Agreement, this DPA and/or other contracts executed by the Parties, including, providing the Service(s) to Customer and providing support and technical maintenance, if agreed in the Agreement.
* For Foldspace to comply with documented reasonable instructions provided by Customer where such instructions are consistent with the terms of the Agreement.
**Duration of Processing.** Subject to any Section of the DPA and/or the Agreement dealing with the duration of the Processing and the consequences of the expiration or termination thereof, Foldspace will Process Personal Data for the duration of the Agreement, unless otherwise agreed upon in writing.
**Type of Personal Data.** Customer may submit Personal Data to the Services, the extent of which is determined and controlled by Customer in its sole discretion, and which may include, but is not limited to the following categories of Personal Data:
* name, email address
* user ID / account identifier
* organization name
* IP address
* application usage metadata (logs, timestamps)
* Any Personal Data or information that the Customer decides to provide to the Foldspace or the Services.
For the avoidance of doubt, the information subject to the Foldspace’s [privacy policy](/privacy/data-privacy/) (e.g., log-in details) shall not be subject to the terms of this DPA.
**Categories of Data Subjects.** Customer may submit Personal Data to the Services, the extent of which is determined and controlled by Customer in its sole discretion, and which may include, but is not limited to Personal Data relating to the following categories of data subjects:
* Customer’s customers and/or Customers
* Customer’s users authorized by Customer to use the Services only to the extent of:
* pseudonymous identifier (UUID), and
* conversation content exchanged with the agent.
**The frequency of the transfer.** Continuous basis.
**The period for which the personal data will be retained, or, if that is not possible, the criteria used to determine that period.** As described in this DPA and/or the Agreement.
**For transfers to (sub-) processors.** As detailed in Schedule 2.
***
## SCHEDULE 2 – SUB-PROCESSOR LIST
[Section titled “SCHEDULE 2 – SUB-PROCESSOR LIST”](#schedule-2--sub-processor-list)
| Entity Name | Sub-Processing Activities | Country of Storage |
| :-------------------------- | :---------------------------------------------------- | :----------------- |
| Google cloud, Gemini | Cloud Service Provider, including Gemini AI. | US |
| Open AI | artificial intelligence and machine learning services | US |
| SendGrid | email communications/updates | US |
| Foldspace Inc, Andrena Ltd. | Provision of the Services and support | Israel and the US |
***
## SCHEDULE 3 - STANDARD CONTRACTUAL CLAUSES
[Section titled “SCHEDULE 3 - STANDARD CONTRACTUAL CLAUSES”](#schedule-3---standard-contractual-clauses)
### EU SCCs
[Section titled “EU SCCs”](#eu-sccs)
If the Processing of Personal Data includes transfers from the EU to countries outside the EEA which do not offer adequate level of data protection or which have not been subject to an Adequacy Decision, the Parties shall comply with Chapter V of the GDPR. The Parties hereby agree to execute the Standard Contractual Clauses as follows:
a) The Standard Contractual Clauses (Controller-to-Processor and Processor to Processor) as applicable, will apply, with respect to restricted transfers between Customer and Foldspace that are subject to the GDPR.
b) The Parties agree that for the purpose of transfer of Personal Data between Customer (as Data Exporter) and Foldspace (as Data Importer), the following shall apply: (i) Clause 7 of the Standard Contractual Clauses shall be not applicable; (ii) In Clause 9, option 2 shall apply and the method described in Section 5 of the DPA (Authorization Regarding Sub-Processors) shall apply; (iii) Clause 11 of the Standard Contractual Clauses shall be not applicable; (iv) In Clause 13: the relevant option applicable to the Customer, as informed by Customer to Foldspace; (v) In Clause 17, option 1 shall apply. The Parties agree that the Standard Contractual Clauses shall be governed by the laws of Ireland; and (vi) In Clause 18(b) the Parties choose the courts of Ireland, as their choice of forum and jurisdiction.
c) Annex I.A: With respect to Module Two: (i) Data Exporter is Customer as a data controller and (ii) the Data Importer is Foldspace as a data processor. With respect to Module Three: (i) Data Exporter is Customer as a data processor and (ii) the Data Importer is Foldspace as a data processor (sub-processor). Data Exporter and Data Importer Contact details: As detailed in the Agreement. Signature and Date: By entering into the Agreement and this DPA, each Party is deemed to have signed these Standard Contractual Clauses incorporated herein, including their Annexes, as of the Effective Date of the DPA.
d) Annex I.B of the Standard Contractual Clauses shall be completed as described in Schedule 1 (Details of the Processing) of this DPA.
e) Annex I.C of the Standard Contractual Clauses shall be completed as follows: The competent supervisory authority is the Irish supervisory authority.
f) Annex II of the Standard Contractual Clauses shall be completed as described in the Security Documentation.
g) Annex III of the Standard Contractual Clauses shall be completed with the authorized sub-processors detailed in Schedule 2 (Sub-processor list) of this DPA.
### UK SCCs
[Section titled “UK SCCs”](#uk-sccs)
If the Processing of Personal Data includes transfers from the UK to countries which do not offer adequate level of data protection or which have not been subject to an Adequacy Decision, the Parties shall comply with Article 45(1) of the UK GDPR and Section 17A of the Data Protection Act 2018. The Parties hereby agree to execute the International Data Transfer Addendum to the EU Commission Standard Contractual Clauses as follows:
a) The UK Standard Contractual Clauses (Controller-to-Processor and Processor to Processor) if applicable, will apply with respect to restricted transfers between Customer and Foldspace that are subject to the GDPR.
b) The Parties agree that for the purpose of transfer of Personal Data between Customer (as Data Exporter) and Foldspace (as Data Importer), the following shall apply: (i) Clause 7 of the Standard Contractual Clauses shall be not applicable; (ii) In Clause 9, option 2 shall apply and the method described in Section 5 of the DPA (Authorization Regarding Sub-Processors) shall apply; (iii) Clause 11 of the Standard Contractual Clauses shall be not applicable; (iv) In Clause 17, option 1 shall apply. The Parties agree that the Standard Contractual Clauses shall be governed by the laws of England and Wales; and (v) In Clause 18(b) the Parties choose the courts of England and Wales. A data subject may also bring legal proceedings against the data exporter and/or data importer before the courts of any country in the UK. The Parties agree to submit themselves to the jurisdiction of such courts, as their choice of forum and jurisdiction. Which Parties may end this Addendum as set out in Section 19: Importer and/or Exporter, in accordance with the agreed terms of the DPA.
c) Annex I.A: With respect to Module Two: Data Exporter is Customer as a data controller and the Data Importer is Foldspace as a data processor. With respect to Module Three: Data Exporter is Customer as a data processor and the Data Importer is Foldspace as a data processor (sub-processor). Data Exporter and Data Importer Contact details: As detailed in the Agreement. Signature and Date: By entering into the Agreement and this DPA, each Party is deemed to have signed these UK Standard Contractual Clauses incorporated herein, including their Annexes, as of the Effective Date of the DPA.
d) Annex I.B of the UK Standard Contractual Clauses shall be completed as described in Schedule 1 (Details of the Processing) of this DPA.
e) Annex I.C of the UK Standard Contractual Clauses shall be completed as follows: The competent supervisory authority is the ICO supervisory authority.
f) Annex II of the UK Standard Contractual Clauses shall be completed as described in the Security Documentation.
g) Annex III of the UK Standard Contractual Clauses shall be completed with the authorized sub-processors detailed in Schedule 2 (Sub-processor list) of this DPA.
# Third-party LLM governance & security framework
> Security controls, data handling protocols, and privacy safeguards for third-party LLM providers used by Foldspace.
## Overview
[Section titled “Overview”](#overview)
Foldspace utilizes a multi-LLM orchestration strategy to deliver agentic product experiences. This document outlines the security controls, data handling protocols, and privacy safeguards enforced when interacting with third-party Large Language Model (LLM) providers.
## 1. Approved LLM Providers
[Section titled “1. Approved LLM Providers”](#1-approved-llm-providers)
Foldspace strictly utilizes Enterprise-grade API endpoints from the following providers:
* **OpenAI**
* **Google Gemini**
## 2. Data Sovereignty & Zero-Training Mandate
[Section titled “2. Data Sovereignty & Zero-Training Mandate”](#2-data-sovereignty--zero-training-mandate)
The core of our security posture is the **Zero-Persistence / Zero-Training** architecture.
* **No Model Training:** Under our enterprise agreements, customer data (prompts, context, and outputs) is **never** used by Foldspace or our third-party providers to train, retrain, or fine-tune base models.
* **Data Isolation:** Customer data is processed in isolated sessions. There is no cross-pollination of “learned” logic between different customer environments.
## 3. PII Handling & Data Privacy Guardrails
[Section titled “3. PII Handling & Data Privacy Guardrails”](#3-pii-handling--data-privacy-guardrails)
Exclusively available in the **Foldspace Enterprise Package**, our automated Privacy Guardrail Layer allows organizations to sanitize data.
### Customer-Controlled Masking Configuration
[Section titled “Customer-Controlled Masking Configuration”](#customer-controlled-masking-configuration)
Admins have granular control to enable and configure masking for specific sensitive fields via the Foldspace management console. This ensures that the AI agent receives only the anonymized context required to perform its task, enforcing the **Principle of Least Privilege**.
Supported masking categories include:
* **Email Addresses:** Obscured to prevent identification while maintaining session utility.
* **Personal Names:** Individual names are masked in conversations and reports to maintain confidentiality.
* **Payment Information:** Secures sensitive financial details, including credit card and bank account numbers.
* **Geographic Locations:** Generalizes or hides specific user location data.
* **IP Addresses:** Conceals IP addresses captured through tracking, recordings, or logs.
## 4. Encryption & Transit Security
[Section titled “4. Encryption & Transit Security”](#4-encryption--transit-security)
* **In-Transit:** All data exchanged between the Foldspace platform and LLM providers is encrypted using **TLS 1.2 or higher**.
* **At-Rest:** Any temporary metadata or session context stored within Foldspace is encrypted using **AES-256** with strict KMS (Key Management Service) rotation policies.
## 5. Action Governance & Authorization
[Section titled “5. Action Governance & Authorization”](#5-action-governance--authorization)
Unlike “Chat-only” AI, Foldspace triggers backend workflows. We ensure this remains secure through:
* **Scoped Permissions:** The AI agent operates using the “Principle of Least Privilege.” It can only access the APIs and actions explicitly defined in your **Action Schema**.
* **Human-in-the-Loop (HITL):** High-stakes actions (e.g., data deletion, financial transactions) require an explicit ‘User Confirmation’ step, preventing autonomous unauthorized actions.
* **Identity Mapping:** Agent actions are tied to the authenticated user’s existing session and permissions.
## 6. Data Collection & Customer Control
[Section titled “6. Data Collection & Customer Control”](#6-data-collection--customer-control)
Foldspace is designed to give customers full control over the data they share.
* **Tracking Code & Mandatory Fields:** Data collection is performed via the Foldspace tracking code. The **only mandatory field** required for operation is a unique **User UUID**.
* **Custom Attributes:** Customers have full control over all other data points. You may choose to send custom attributes—such as user roles or subscription tiers—to provide deeper personalization or contextual responses.
* **Optional User Data Exclusion:** Customers may choose to exclude end-user or subscription-level data entirely, ensuring no metadata is sent to Foldspace.
***
For a copy of our SOC 2 report or detailed Data Processing Addendum (DPA), please contact .
# PII Control
> Mask personally identifiable information per agent to maintain user privacy and comply with data protection regulations.
Protecting Personally Identifiable Information (PII) is essential for maintaining user privacy and complying with data protection regulations.
Foldspace provides PII Control settings so you can mask sensitive information. Masking is configured **per agent**, so each of your agents can have its own PII rules.
## What is PII?
[Section titled “What is PII?”](#what-is-pii)
Personally Identifiable Information (PII) refers to any data that can be used to identify an individual. Examples include email addresses, names, financial details, locations, and IP addresses.
## How to Set Up PII Masking
[Section titled “How to Set Up PII Masking”](#how-to-set-up-pii-masking)
To enable PII masking:
1. **Go to Settings:** Log in to your Foldspace account, click on the Settings menu, and select **PII Control** from the left-hand panel.
2. **Choose an agent:** PII masking is set per agent, so select the agent you want to configure.
3. **Toggle Masking Options:** For that agent, switch on the types of PII you want to protect.
4. **Save Changes:** Click **Save** to apply masking rules.
## Available Masking Options
[Section titled “Available Masking Options”](#available-masking-options)
| PII Type | Description |
| :------------------- | :-------------------------------------------------------------------------------- |
| Email Addresses | Obscure email addresses to prevent identification and maintain privacy. |
| Names | Mask individuals’ names in conversations and reports to maintain confidentiality. |
| Payment Information | Secure sensitive financial details like credit card and bank account numbers. |
| Geographic Locations | Hide or generalize specific user location data to protect privacy. |
| IP Addresses | Conceal IP addresses captured through tracking, recordings, or logs. |
# Schedule A — SLA (Service Level Agreement)
> Target service levels for availability and support responsiveness of the Foldspace service.
This SLA outlines the target service levels for the Service. It defines Company’s goals for availability and support responsiveness. These targets are not guaranteed and are provided for transparency only.
Features labeled as beta, trial, preview, or development environments are excluded. This SLA does not apply during periods when the Customer is in breach of the Agreement, including failure to pay.
## 1. Definitions
[Section titled “1. Definitions”](#1-definitions)
### 1.1. Business Days
[Section titled “1.1. Business Days”](#11-business-days)
Monday through Friday, excluding holidays.
### 1.2. Business Hours
[Section titled “1.2. Business Hours”](#12-business-hours)
Monday through Friday, 9:00 AM – 5:00 PM EST, excluding U.S. public holidays.
### 1.3. Maintenance
[Section titled “1.3. Maintenance”](#13-maintenance)
Total time during which the Service is unavailable, excluding defined SLA exclusions.
### 1.4. Measured Period
[Section titled “1.4. Measured Period”](#14-measured-period)
The total number of minutes in a calendar month.
### 1.5. Scheduled Maintenance
[Section titled “1.5. Scheduled Maintenance”](#15-scheduled-maintenance)
Maintenance for which:
(i) Customer is notified at least 48 hours in advance, or
(ii) It occurs during a published standard maintenance window.
### 1.6. Uptime
[Section titled “1.6. Uptime”](#16-uptime)
Minutes during which the Service (including API) is available for use.
### 1.7. Uptime Percentage
[Section titled “1.7. Uptime Percentage”](#17-uptime-percentage)
Calculated as: Uptime Percentage = X / (Y – Z) × 100
Where:
* X = Uptime
* Y = Total minutes in Measured Period
* Z = Minutes of SLA Exclusions during that period
## 2. Service Availability
[Section titled “2. Service Availability”](#2-service-availability)
### 2.1. Uptime Commitment
[Section titled “2.1. Uptime Commitment”](#21-uptime-commitment)
For services hosted by Company (not self-hosted by Customer), the target Uptime Percentage is **99.5%** each calendar month.
### 2.2. Scheduled Maintenance
[Section titled “2.2. Scheduled Maintenance”](#22-scheduled-maintenance)
Company aims to schedule maintenance during off-peak hours (e.g., weekends or late nights). Scheduled Maintenance will not exceed **4 hours per month**.
### 2.3. Unscheduled Maintenance
[Section titled “2.3. Unscheduled Maintenance”](#23-unscheduled-maintenance)
Company will:
* Provide at least 1 hour’s notice for urgent, non-scheduled maintenance when feasible
* Conduct unscheduled maintenance only as needed to address urgent issues (e.g., availability or security threats)
## 3. Support
[Section titled “3. Support”](#3-support)
### Contacting Support
[Section titled “Contacting Support”](#contacting-support)
To open a support ticket, Customer must email . Company may, at any time, update the communication methods to be used in order to submit the request to Company’s support team and to respond to the Customer.
The email must include at the minimum, the following information:
* Customer name;
* Contact information including name, email and phone number where Customer contact can be reached; and
* Description of the Service feature or function affected and the incident which requires support, including the name of the specific customer dashboard affected.
Support is available during **Business Hours**.
In order to be addressed by Company, problems must be verifiable and reproducible. Furthermore, in order for Company to address a support request, Customer must provide Company with all information, documentation, assistance and access as Company might reasonably require, including, without limitation:
* setup information,
* application knowledge,
* listing of any output,
* detailed steps required to enable Company to replicate the problem,
* exact wording of problem messages, and
* any other data that Company may reasonably request in order to reproduce operating conditions similar to those present when the problem occurred.
### 3.1. Definition of Severity Levels
[Section titled “3.1. Definition of Severity Levels”](#31-definition-of-severity-levels)
Any support ticket opened by the Customer will be categorized by Company according to the following definitions:
**Severity Level 1 (Critical):** Service failures or critical bugs affecting all users, requiring immediate action. Examples: Dashboard is down, significant data loss, other major recurring issues
**Severity Level 2 (High):** Issues impacting functionality but not rendering the Service unusable. Examples: Major feature malfunctions, performance issues affecting many users
**Severity Level 3 (Low):** Minor inconveniences or cosmetic problems that do not affect Service functionality. Examples: Service hierarchy issues, brand name issues.
**Severity Level 4 (Inquiry):** Informational requests or feature suggestions.
### 3.2. Response and Resolution Times
[Section titled “3.2. Response and Resolution Times”](#32-response-and-resolution-times)
In the event of a failure of the Service to function in any material respect with its applicable performance specifications specified in the Agreement, Company shall, during its Business Hours, use its commercially reasonable efforts to respond and resolve (such as, by providing a workaround or a patch) to properly submitted support requests based on the following severity levels:
| Severity Level | Acknowledgement Time | Estimated Resolution Time |
| :----------------- | :------------------- | :------------------------ |
| Level 1 (Critical) | 2 Business Hours | Within 24 Business Hours |
| Level 2 (High) | 8 Business Hours | Within 72 Business Hours |
| Level 3 (Low) | 2 business days | Within 5 business days |
| Level 4 (Inquiry) | 3 business days | N/A (\*) |
(\*) For clarity, Company makes no commitment as to any feature requests. Any requests made for new features or functionality will be taken into consideration and may be implemented at a future date at Company’s sole discretion.
### 3.3. Support Exclusions
[Section titled “3.3. Support Exclusions”](#33-support-exclusions)
The technical support described above will only be provided with respect to the Service version which is under support, and, in addition to the SLA Exclusions specified below, Company shall not be required to correct any error that in Company’s reasonable discretion resulting from:
(i) any modifications of the Service that have not been approved by the Company in writing;
(ii) Customer’s instructions, or installation or set up adjustments;
(iii) use of the Service other than as permitted in the Agreement;
(iv) any fault in any equipment or programs used in conjunction with the Service, or other causes beyond the control of the Company; and/or
(v) Customer’s negligence or willful misconduct.
### 3.4. SLA Exclusions
[Section titled “3.4. SLA Exclusions”](#34-sla-exclusions)
Company’s obligations hereunder are based on and subject to the Customer:
(i) complying with the terms and conditions of the Agreement, including this SLA;
(ii) complying with Company’s instructions, if any, for performing any corrective action; and
(iii) maintaining the connectivity (with acceptable bandwidth) of the Customer’s workstations to the main internet, as well as creating and maintaining firewall definitions and opening required ports that permit access to the Service.
The following shall not be considered within the definition or calculation of Uptime:
* Scheduled Maintenance;
* backups of the Service; and
* Service unavailability that is attributable to:
* (A) causes beyond Company’s reasonable control, such as a force majeure event, or the performance of any third-party hosting provider or communications or internet service provider;
* (B) Customer’s failure to perform any obligation under the Agreement or this SLA that affects the performance of the Service;
* (C) any actions or omissions of the Customer or any third party acting on its behalf;
* (D) Customer’s or any third party’s products, services, data, or technology;
* (E) Service unavailability caused by the suspension or termination of Customer’s right to use the Service in accordance with the Agreement; and/or
* (F) separate instances of Service unavailability of less than five (5) minutes duration each (each of (A) through (F) above, an “SLA Exclusion”).
# Terms and conditions
> Legal terms governing access, usage, and responsibilities when using Foldspace services.
These Foldspace AI Terms of Service (the “Agreement”) apply to, and govern, access to and use of the Service (defined below).
CUSTOMER IS AGREEING TO THIS AGREEMENT BY CLICKING ON THE “I ACCEPT” (OR SIMILAR) BUTTON, BY CHECKING A CHECKBOX FOR THE ACCEPTANCE OF THIS AGREEMENT, OR OTHERWISE BY REGISTERING FOR OR ACCESSING THE SERVICE, WHICHEVER IS EARLIER.
This Agreement also applies to, and governs, the executed Order (defined below), and this Agreement is hereby incorporated by reference into, and made a part of, such Order.
The Agreement constitutes a binding agreement between Foldspace.ai (“Company”) and the customer specified in the Order or the Service registration page, as the case may be (“Customer”). If an individual is submitting an Order, or otherwise subscribing to the Service, using an organization’s email address, such organization shall be deemed the Customer.
Company and Customer may be collectively referred to herein as the “Parties”, and each individually as a “Party”. An individual entering into this Agreement on behalf of the Customer, represents that he/she has the right, authority and capacity to act on behalf of the Customer and to bind the Customer to this Agreement.
If Customer has purchased its Service subscription through a Reseller (defined below), Customer’s payment obligations under Section 7 (Payment) shall not apply. In such cases: (a) Company will only be obligated to provide the Service to Customer if Company and Reseller have executed a purchase order for such purchase; (b) Company may share information with Reseller related to Customer’s use and consumption of the Service; (c) Company shall be entitled to withhold or otherwise suspend Customer’s access to the Service if Company has not been paid by Reseller; (d) this Agreement governs Customer’s access to and use of the Service, notwithstanding anything to the contrary in Customer’s agreement with the Reseller; and (e) Reseller is not authorized to make any changes to this Agreement or otherwise authorized to make any warranties, representations, promises or commitments on behalf of Company or in any way concerning the Service.
## 1. DEFINITIONS
[Section titled “1. DEFINITIONS”](#1-definitions)
**1.1 Affiliate** means, with respect to a Party, any entity that directly or indirectly controls, is controlled by, or is under common control with such Party, whereby “control” means the possession, directly or indirectly, of the power to direct, or cause the direction of, the management and policies of such person, whether through the ownership of voting securities, by contract, or otherwise.
**1.2 Content** means any text, data, information, reports, files, images, graphics, software code, or other content.
**1.3 Customer Content** means any Content submitted or uploaded to, or transmitted through, the Service, or otherwise provided or made available to Company, by or on behalf of Customer.
**1.4 Customer Environment** means the on-premise or virtual equipment, systems and/or servers owned or managed solely by Customer, as specified in the Order.
**1.5 Documentation** means the Service-related operational guides or manuals, which Company provides or makes available to Customer, in any form or medium. Documentation does not include any marketing, or other publicly available, materials.
**1.6 Effective Date** means the date the Order is executed by the Parties, unless the Order itself specifies a different start/effective date.
**1.7 Input** means any Customer Content inputted or otherwise submitted by Customer to the Service in order to receive an Output.
**1.8 Intellectual Property Rights** means any and all rights, titles, and interests (under any jurisdiction or treaty, whether protectable or not, registered or unregistered, and whether vested, contingent, or future) in and to inventions, works of authorship, designs, software, technical info, databases, know-how, branding, and includes patents, copyrights, trade secrets, trademarks, and associated goodwill.
**1.9 Installed Software** means any client device software made available to Customer by Company for installation on Users’ devices, to be used in connection with the Service.
**1.10 Law** means any federal, state, foreign, regional, or local statute, regulation, ordinance, or rule of any jurisdiction.
**1.11 Order** means the ordering document (e.g., Order Form, Proposal) entered into between the Parties, specifying the Service and any other services purchased.
**1.12 Other Services** means Setup Services, Support Services, Professional Services, and/or any other services (other than the Service) provided by Company.
**1.13 Output** means any Content generated specifically for Customer by the Service in response to the Customer’s Input, excluding any Company IP.
**1.14 Privacy Policy** means the Company’s privacy policy, available [here](/privacy/data-privacy/).
**1.15 Professional Services** means Service-related installation, deployment, configuration, training, customization, integration, or other services.
**1.16 Reseller** means any distributor, reseller, or similar channel partner authorized by Company to sell Service subscriptions.
**1.17 Service** means the Company’s SaaS offering, known as Foldspace AI, including related applications, APIs, AI agents, and tools.
**1.18 Service Content** means any Content (excluding Customer Content) provided or made available via the Service.
**1.19 Subscription Scope** means any Service-related usage or consumption limitations or entitlements specified in the Order.
**1.20 Site** means the Company’s website:
**1.21 Support Services** means any Service-related technical support and maintenance services specified in the Service Level Agreement (“SLA”) set forth in [Schedule A](/privacy/sla/).
**1.22 Usage Statistics** means any non-Customer-identifying data or analytics relating to the use and operation of the Service.
**1.23 User** means Customer’s or its Affiliate’s employees or contractors authorized to use the Service under an issued user ID and password.
## 2. ACCOUNT
[Section titled “2. ACCOUNT”](#2-account)
In order to access the Service, Customer may be given the opportunity (or otherwise be required) to generate an account by submitting the information requested in the applicable online registration page or Service interface (the “Account”).
Customer’s Account registration may impose limitations on the number or types of Accounts. Absent such limitations, Customer shall be entitled to:
* A single administrator Account that will have administrative privileges over the Account (the “Admin Account”), and
* One user Account per user (each, a “User Account”).
Customer shall ensure that all information submitted during the registration process is, and will thereafter remain, complete and accurate.
As between Company and Customer, Customer shall be solely responsible and liable for:
* Maintaining the confidentiality and security of its Account credentials, and
* All activities that occur under or in such Account.
Customer shall immediately notify Company in writing of any unauthorized access to, or use of, an Account, or any other breach of security.
## 3. PILOTS AND EVALUATION PRODUCTS
[Section titled “3. PILOTS AND EVALUATION PRODUCTS”](#3-pilots-and-evaluation-products)
### 3.1. Pilots
[Section titled “3.1. Pilots”](#31-pilots)
If agreed in the Order, Customer may be entitled to conduct an evaluation, “proof-of-concept”, or pilot of the Service (a “Pilot”). A Pilot is limited to whatever duration, features, and functionalities Company elects in its sole discretion (or that is otherwise specified in the Order), and—unless agreed otherwise—Company reserves the right to add or remove features and terminate the Pilot at any time, with or without notice.
### 3.2. Evaluation Products
[Section titled “3.2. Evaluation Products”](#32-evaluation-products)
From time to time, Company may permit Customer to try certain Service features or functionalities at no charge for a free trial or evaluation period (each, an “Evaluation Product”). Evaluation Products may be labeled beta, pilot, evaluation, or trial. Unless otherwise specified, the default Evaluation Period is thirty (30) days. Company reserves the right to terminate an Evaluation Period at any time, with or without notice.
### 3.3. General
[Section titled “3.3. General”](#33-general)
The usage restrictions in Section 9.2 (Usage Restrictions) also apply to Evaluation Products and Pilots.
**IMPORTANT:** Evaluation Products and Pilots are provided solely for internal evaluation and not for production use. Company shall have no obligation or liability of any kind for Evaluation Products or Pilots. To the extent that applicable law does not permit full exclusion of liability, Company’s aggregate liability shall not exceed ten US dollars (USD $10).
## 4. SERVICE SUBSCRIPTION
[Section titled “4. SERVICE SUBSCRIPTION”](#4-service-subscription)
### 4.1. General
[Section titled “4.1. General”](#41-general)
Subject to the terms of this Agreement (including timely payment of all applicable Fees), Company grants Customer a limited, non-exclusive, non-transferable, non-sublicensable right and license, during the Subscription Term, to:
(a) Access and use the Service, and view the Service Content, for Customer’s internal end use; and
(b) If applicable, download, install, and use the Installed Software within the applicable Customer Environment.
The Subscription is subject to the defined Subscription Scope. Customer may not use technical or other means to exceed or circumvent such limits.
The Service is licensed, not sold. All rights not explicitly granted are reserved by Company.
Company will use reasonable efforts to provide the Service as intended. Downtime may occur for scheduled or emergency maintenance, third-party outages, or causes beyond Company’s control.
Customer remains responsible for its Users’ compliance with this Agreement. Acts or omissions by Users are deemed those of the Customer.
### 4.2. Customer Affiliates
[Section titled “4.2. Customer Affiliates”](#42-customer-affiliates)
Subject to the Subscription Scope, Customer may permit Affiliates to use the Service, provided:
(a) Such use is for the benefit of Customer or the Affiliate; and
(b) The Affiliate agrees to be bound by all applicable restrictions.
Customer remains fully responsible for Affiliate and User compliance.
### 4.3. Monitoring
[Section titled “4.3. Monitoring”](#43-monitoring)
Company and its Affiliates may monitor Customer’s use of the Service (e.g., logs, analytics) for:
* Quality control
* Agreement enforcement
* Product improvement
* Generating Usage Data
### 4.4. Delivery and Hosting
[Section titled “4.4. Delivery and Hosting”](#44-delivery-and-hosting)
The Service will be provided via Site, API, or other method defined in the Order. Software (e.g., Agents) is deemed accepted upon delivery. The Service may be hosted by a third-party Hosting Provider, and:
(a) Availability may depend on their systems; and
(b) Customer Content may be processed by them.
### 4.5. Usage Data
[Section titled “4.5. Usage Data”](#45-usage-data)
Company may generate, use, and commercialize Usage Data, and use it to improve the Service and train models. This activity is not limited by this Agreement.
### 4.6. Features and Functionalities
[Section titled “4.6. Features and Functionalities”](#46-features-and-functionalities)
Company may update or replace features and UI elements over time. Material features Customer is entitled to will not be removed unless enhanced. Customer’s purchase is not contingent on future features or functionalities.
## 5. AI AGENT FUNCTIONALITY
[Section titled “5. AI AGENT FUNCTIONALITY”](#5-ai-agent-functionality)
### 5.1. Overview
[Section titled “5.1. Overview”](#51-overview)
As part of the Service, Company may provide functionality enabling Customer and its Users to interact with AI-powered software agents and automations (“AI Agents”). These agents may perform tasks on Customer’s behalf, including but not limited to:
* Scheduling and booking
* Initiating UI displays
* Updating or retrieving records
* Accessing third-party systems (e.g., CRM, knowledge bases)
* Monitoring user activity (including screen recording with data masking, if enabled)
### 5.2. Customer Responsibilities
[Section titled “5.2. Customer Responsibilities”](#52-customer-responsibilities)
**a. Authorization and Responsibility**
Customer is solely responsible for ensuring it has all necessary rights and consents for AI Agents to interact with systems, applications, and users. Customer remains liable for any actions taken by AI Agents and their consequences.
**b. Verification and Review**
Customer must independently validate all AI Agent outputs and actions. Company does not guarantee the correctness, legality, or suitability of any action or recommendation generated by an AI Agent.
**c. Screen Recording and User Tracking**
If enabled, screen recording and tracking features must comply with applicable laws and internal policies. Customer must obtain all necessary consents.
**d. Third-Party System Interactions**
AI Agents may interact with third-party systems. Company is not responsible for the behavior, data practices, or results of such interactions.
**e. AI Agent Outputs and Disclaimer**
AI-generated outputs may be inaccurate or incomplete. They are provided “as is” without warranties, and Customer assumes all risk. Customer must evaluate and verify all outputs before acting on them.
### 5.3. Lawful Use and Prohibited Activities
[Section titled “5.3. Lawful Use and Prohibited Activities”](#53-lawful-use-and-prohibited-activities)
Customer must ensure use of AI Agents complies with all applicable laws, including AI-specific regulations. Customer shall not use AI Agents to:
* Violate laws or third-party rights
* Submit or process Sensitive Data
* Mislead users into believing AI-generated content is human
* Reverse engineer or extract AI models
* Train competing AI products
Company may suspend AI features without notice for suspected misuse.
### 5.4. Automated Decision-Making Restrictions
[Section titled “5.4. Automated Decision-Making Restrictions”](#54-automated-decision-making-restrictions)
Customer shall not use AI Agents for automated decisions with legal or significant effects (e.g., employment, credit, healthcare) without human oversight.
### 5.5. Disclaimers
[Section titled “5.5. Disclaimers”](#55-disclaimers)
This section supplements all disclaimers in Sections 8 and 11. Company assumes no liability for AI Agent behavior or content.
### 5.6. AI Training
[Section titled “5.6. AI Training”](#56-ai-training)
Unless explicitly authorized in writing, Company will not use Customer Content or Outputs to train its AI models. Aggregated, de-identified statistics may be used solely to improve AI functionality for the Customer.
### 5.7. Third-Party AI Providers
[Section titled “5.7. Third-Party AI Providers”](#57-third-party-ai-providers)
Company may use third-party AI providers to deliver certain AI features. Such providers will not use Customer data to train their own models. Company disclaims all liability for third-party AI providers’ actions.
## 6. SERVICES
[Section titled “6. SERVICES”](#6-services)
### 6.1. Setup Services
[Section titled “6.1. Setup Services”](#61-setup-services)
If applicable and subject to payment of applicable Fees, Company will provide the setup or onboarding services specified in the Order (“Setup Services”).
### 6.2. Support Services
[Section titled “6.2. Support Services”](#62-support-services)
As long as Customer remains current with all payment obligations, Customer is entitled to receive Support Services as specified in the Agreement.
### 6.3. Professional Services
[Section titled “6.3. Professional Services”](#63-professional-services)
Except for Setup Services, Company is not obligated to provide Professional Services unless mutually agreed in a signed Professional Services Statement of Work (each, a “Professional Services SOW”). Professional Services will be charged in accordance with the applicable SOW and deemed incorporated into this Agreement.
### 6.4. General
[Section titled “6.4. General”](#64-general)
Other Services will be performed by Company or its Affiliates for the benefit of the Customer. Customer agrees to:
* Cooperate fully and make necessary systems, assets, and resources available.
* Allow Company to subcontract Other Services with Customer’s reasonable written consent (not to be unreasonably withheld).
Unless expressly agreed otherwise, Other Services will be delivered remotely. If on-site attendance is requested and approved, Company may charge standard rates and recover travel and lodging expenses.
## 7. PAYMENT
[Section titled “7. PAYMENT”](#7-payment)
### 7.1. Fees
[Section titled “7.1. Fees”](#71-fees)
Customer agrees to pay the fees and charges set forth in the Order (“Fees”).
### 7.2. Fee Increases
[Section titled “7.2. Fee Increases”](#72-fee-increases)
Company may increase Fees for renewals with at least 30 days’ prior written notice.
### 7.3. Payment Terms
[Section titled “7.3. Payment Terms”](#73-payment-terms)
Unless otherwise stated:
* Fees are in USD and billed annually.
* Company may invoice in advance.
* Payment is due within 30 days.
* All payments are non-refundable.
* Late payments accrue 1.5% monthly interest or the maximum allowed by law.
* Company may charge for collection costs (legal, court, etc.).
### 7.4. Payment Disputes
[Section titled “7.4. Payment Disputes”](#74-payment-disputes)
Customer must dispute charges in writing within 7 days of invoice receipt or waive the right to dispute.
### 7.5. Taxes
[Section titled “7.5. Taxes”](#75-taxes)
Customer is responsible for all applicable taxes except for those based on Company’s income.
### 7.6. Payment Processing
[Section titled “7.6. Payment Processing”](#76-payment-processing)
Customer consents to recurring charges and updates to stored payment info for uninterrupted service.
### 7.7. Reporting; Usage Audit
[Section titled “7.7. Reporting; Usage Audit”](#77-reporting-usage-audit)
Company may audit usage and charge for overages.
## 8. CUSTOMER CONTENT AND OUTPUT
[Section titled “8. CUSTOMER CONTENT AND OUTPUT”](#8-customer-content-and-output)
**8.1. Ownership.** Customer retains ownership of all Customer Content.
**8.2. Output Ownership.** Customer owns Output generated from their Input.
**8.3. No Sensitive Data.** Customer must not upload or link to sensitive data (e.g., health, financial, political).
**8.4. Responsibility.** Customer is responsible for legality, accuracy, and quality of its Content.
**8.5. License to Company.** Customer grants Company a license to process Content for service delivery and anonymized improvement.
**8.6. Output Disclaimer.** Customer is responsible for verifying Output accuracy. AI results are probabilistic and may not be unique.
**8.7. No Storage.** The Service is not intended for data storage. Customer must back up all data.
**8.8. Security.** Company will implement administrative, technical, and physical safeguards to protect Customer Content.
**8.9. Privacy Policy.** Customer consents to the Company’s privacy policy as incorporated by reference.
## 9. COMPANY INTELLECTUAL PROPERTY
[Section titled “9. COMPANY INTELLECTUAL PROPERTY”](#9-company-intellectual-property)
### 9.1. Ownership
[Section titled “9.1. Ownership”](#91-ownership)
Company (and its licensors) retains all rights to the following:
* The Service and its underlying technology
* Other Services
* Service Content (excluding Output)
* Confidential Information
* Feedback
* Usage Statistics
* Any improvements or derivatives of the above
If not automatically vested, rights are assigned to Company, and Customer agrees to assist in perfecting such rights.
### 9.2. Usage Restrictions
[Section titled “9.2. Usage Restrictions”](#92-usage-restrictions)
Customer agrees not to:
* Copy, sell, sublicense, display, or modify any Company IP
* Reverse engineer or extract source code
* Interfere with security features
* Use the Service to build a competing product
* Submit malware or scrape the platform
* Abuse system resources
* Benchmark or publish performance data
* Use the Service for unethical or illegal activity
## 10. CONFIDENTIALITY
[Section titled “10. CONFIDENTIALITY”](#10-confidentiality)
Each party agrees to:
* Protect the other’s Confidential Information using reasonable care
* Use it only for purposes under this Agreement
* Share it only with those who need access and are bound by confidentiality
Disclosures required by law must be preceded by notice where legally allowed. Breach of confidentiality entitles the harmed party to seek equitable relief.
## 11. PERFORMANCE WARRANTY; DISCLAIMERS
[Section titled “11. PERFORMANCE WARRANTY; DISCLAIMERS”](#11-performance-warranty-disclaimers)
### 11.1. Performance Warranty
[Section titled “11.1. Performance Warranty”](#111-performance-warranty)
Company warrants the Service will conform to its documentation and perform services in a professional manner. This does not apply to issues caused by:
* Customer misuse or modification
* Third-party components
* Outdated versions
### 11.2. Disclaimer of Warranties
[Section titled “11.2. Disclaimer of Warranties”](#112-disclaimer-of-warranties)
Except as provided above, all materials are provided “as is.” Company disclaims all implied warranties, including:
* Merchantability
* Fitness for purpose
* Non-infringement
* Data transmission reliability
* Legal compliance
## 12. LIMITATION OF LIABILITY
[Section titled “12. LIMITATION OF LIABILITY”](#12-limitation-of-liability)
### 12.1. Excluded Damages
[Section titled “12.1. Excluded Damages”](#121-excluded-damages)
Neither party is liable for:
* Indirect, incidental, or consequential damages
* Loss of profit, data, or goodwill
* Cost of substitute goods/services
### 12.2. Cap on Liability
[Section titled “12.2. Cap on Liability”](#122-cap-on-liability)
Company’s total liability is limited to the amount paid by Customer in the 12 months before the event giving rise to liability, or $100 if no fees were paid.
### 12.3. Applicability
[Section titled “12.3. Applicability”](#123-applicability)
These limitations apply regardless of legal theory and even if a remedy fails its essential purpose.
## 13. INDEMNIFICATION
[Section titled “13. INDEMNIFICATION”](#13-indemnification)
### 13.1. By Company
[Section titled “13.1. By Company”](#131-by-company)
Company will defend and indemnify Customer against claims that the Service infringes a third-party copyright or patent, except if caused by:
* Modifications by Customer
* Combination with third-party products
* Misuse or outdated versions
If the Service becomes subject to a claim, Company may:
* Get a license
* Modify the Service
* Terminate access and refund prepaid unused fees
### 13.2. By Customer
[Section titled “13.2. By Customer”](#132-by-customer)
Customer will defend and indemnify Company against claims arising from:
* Customer’s breach of the Agreement
* Misuse of the Service
### 13.3. Procedure
[Section titled “13.3. Procedure”](#133-procedure)
The indemnified party must promptly notify the other and allow full control of defense and settlement, providing cooperation and not admitting liability.
## 14. TERM AND TERMINATION
[Section titled “14. TERM AND TERMINATION”](#14-term-and-termination)
### 14.1. Term
[Section titled “14.1. Term”](#141-term)
This Agreement begins on the Effective Date and continues through the Subscription Term unless terminated.
### 14.2. Renewals
[Section titled “14.2. Renewals”](#142-renewals)
Unless stated otherwise, subscriptions automatically renew for equal periods unless either party gives 30 days’ written notice.
### 14.3. Termination
[Section titled “14.3. Termination”](#143-termination)
Either party may terminate:
* For cause with 30 days’ notice if the other party fails to cure a material breach
* Immediately for bankruptcy, insolvency, or cessation of business
Company may terminate for convenience with 30 days’ notice and refund unused prepaid fees.
### 14.4. Suspension
[Section titled “14.4. Suspension”](#144-suspension)
Company may suspend access if:
* Payment is 7+ days late
* There’s a material breach
* Suspension is necessary to protect the platform
* Required by law
### 14.5. Effect of Termination
[Section titled “14.5. Effect of Termination”](#145-effect-of-termination)
Upon termination:
* Access ceases
* All unpaid fees are due
* Customer may retrieve content for 30 days
Afterward, Company may delete Customer Content.
### 14.6. Survival
[Section titled “14.6. Survival”](#146-survival)
Sections 8 through 14 survive termination.
## 15. MISCELLANEOUS
[Section titled “15. MISCELLANEOUS”](#15-miscellaneous)
**15.1. Entire Agreement.** This Agreement supersedes all prior agreements and forms a complete understanding.
**15.2. Amendment.** Only written changes signed by both parties are valid.
**15.3. Precedence.** In case of conflict, this Agreement overrides any Order or SOW unless explicitly stated otherwise.
**15.4. Messages.** Customer is responsible for all messages sent through the Service and their compliance with applicable laws.
**15.5. Third-Party Content.** Company is not responsible for content or systems provided by third parties. Customer assumes risk and compliance responsibility.
**15.6. Third-Party Software.** Open source components are governed by their respective licenses.
**15.7. Assignment.** Customer may not assign this Agreement without consent. Company may assign freely.
**15.8. Governing Law; Jurisdiction.** This Agreement is governed by California law. Any legal actions must be brought in state or federal courts in San Francisco County, CA.
**15.9. Severability.** If any provision is held invalid, the rest remains enforceable.
**15.10. Publicity.** Company may use Customer’s name and logo in marketing. Customer agrees to participate in reasonable reference activities.
**15.11. Waiver.** No waiver is valid unless in writing. Failure to enforce a right is not a waiver.
**15.12. Supplemental Terms.** Certain features may have additional terms which are binding.
**15.13. No Third-Party Beneficiaries.** Only the parties to this Agreement have rights under it.
**15.14. Relationship.** The parties are independent contractors.
**15.15. Force Majeure.** Neither party is liable for delays or failures due to events beyond their control.
**15.16. Notices.** Notices must be in writing and may be sent via email, mail, or account interface.
**15.17. Export Compliance.** Customer agrees not to violate export laws and sanctions.
**15.18. Customer Resources.** Customer is responsible for all systems and assets needed to access the Service.
**15.19. Expenses.** Each party bears its own costs unless otherwise stated.
**15.20. Government Use.** The Service is commercial software and subject to standard federal government terms.
**15.21. Equal Drafting & Essential Terms.** This Agreement is to be interpreted as if both parties equally participated in drafting.
**15.22. Subpoenas.** Company may disclose Customer data when required by law or court order.
**15.23. High-Risk Activities.** Customer shall not use the Service for high-risk activities (e.g., life support, critical infrastructure).
**15.24. Anti-Corruption.** Customer affirms that no improper inducements or payments have been offered or received.
# Agent Event
> Listen to agent lifecycle, UI, and conversation events from your app with .on() and .off().
Subscribe to agent lifecycle, UI, and conversation events directly from your app.
Prerequisites
The [SDK is installed and initialized](/start/install/). Call these methods inside the `foldspace('when', 'ready', ...)` callback.
## Subscribe to events
[Section titled “Subscribe to events”](#subscribe-to-events)
### `on(event, handler)` / `off(event, handler)`
[Section titled “on(event, handler) / off(event, handler)”](#onevent-handler--offevent-handler)
```javascript
foldspace.agent("AGENT_NAME").on(event, handler);
foldspace.agent("AGENT_NAME").off(event, handler);
```
| Parameter | Type | Description |
| --------- | -------- | ----------------------------------------- |
| `event` | string | The event name. |
| `handler` | function | Callback that receives the event payload. |
Both `.on()` and `.off()` return the agent instance, so calls can be chained.
## Available events
[Section titled “Available events”](#available-events)
### Agent lifecycle
[Section titled “Agent lifecycle”](#agent-lifecycle)
| Event | Payload | Description |
| --------------- | ------- | ---------------------------------------------------- |
| `agent.ready` | `{}` | Fired when the agent is fully initialized and ready. |
| `agent.removed` | `{}` | Fired when the agent is removed from the page. |
### UI lifecycle
[Section titled “UI lifecycle”](#ui-lifecycle)
| Event | Payload | Description |
| ----------- | ------------------- | -------------------------------------------------- |
| `ui.open` | `{ rect: DOMRect }` | Fired when the chat UI is opened. |
| `ui.close` | `{ rect: DOMRect }` | Fired when the chat UI is closed. |
| `ui.show` | `{ rect: DOMRect }` | Fired when the agent becomes visible. |
| `ui.hide` | `{ rect: DOMRect }` | Fired when the agent is hidden. |
| `ui.move` | `{ rect: DOMRect }` | Fired when the agent is dragged to a new position. |
| `ui.resize` | `{ rect: DOMRect }` | Fired when the agent is resized. |
### Conversation lifecycle
[Section titled “Conversation lifecycle”](#conversation-lifecycle)
| Event | Payload | Description |
| ---------------------- | ---------------------------- | ----------------------------------------- |
| `conversation.created` | `{ conversationId: string }` | Fired when a new conversation is created. |
### Action callback lifecycle
[Section titled “Action callback lifecycle”](#action-callback-lifecycle)
| Event | Payload | Description |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `action.callback` | `{ actionKey: string; status: "executed" \| "rendered" \| "failed" \| "finished" \| "clicked" \| "cancelled" \| "not_found"; payload?: any; }` | Fired when an action callback is triggered or changes status. |
### Share data lifecycle
[Section titled “Share data lifecycle”](#share-data-lifecycle)
| Event | Payload | Description |
| ----------------------- | --------------------- | ----------------------------------------------- |
| `shareData.shareState` | `{ active: boolean }` | Fired when the data sharing state is updated. |
| `shareData.shareScreen` | `{ active: boolean }` | Fired when the screen sharing state is updated. |
## Match events with wildcards
[Section titled “Match events with wildcards”](#match-events-with-wildcards)
Listen to multiple events at once using wildcard patterns.
| Pattern | Matches | Example |
| ---------------- | ------------------------------- | ------------------------------------------------------- |
| `*` | All events | `foldspace.agent("demo").on("*", handler)` |
| `ui.*` | All UI lifecycle events | `foldspace.agent("demo").on("ui.*", handler)` |
| `conversation.*` | All conversation-related events | `foldspace.agent("demo").on("conversation.*", handler)` |
Listen to a group of events.
```javascript
foldspace.agent("demo").on("ui.*", (payload) => {
console.log("UI event triggered:", payload);
});
```
Use `*` to listen to all events. The handler also receives the event name as a second argument.
```javascript
foldspace.agent("demo").on("*", (payload, eventName) => {
console.log(`Event "${eventName}" fired with:`, payload);
});
```
## Remove listeners
[Section titled “Remove listeners”](#remove-listeners)
To remove a specific listener, call `.off()` with the same event name and handler.
```javascript
const handler = (payload) => console.log("UI opened:", payload);
foldspace.agent("demo").on("ui.open", handler);
// Later...
foldspace.agent("demo").off("ui.open", handler);
```
If you used a wildcard (`*` or `group.*`), `.off()` removes the same handler for all matching events.
## Chain listeners
[Section titled “Chain listeners”](#chain-listeners)
Chain listeners across lifecycle, UI, and conversation events.
```javascript
foldspace.agent("support")
.on("agent.ready", () => console.log("Agent is ready"))
.on("ui.open", ({ rect }) => console.log("Chat opened at:", rect))
.on("conversation.created", ({ conversationId }) =>
console.log("New conversation:", conversationId)
);
```
## Related
[Section titled “Related”](#related)
* [Messaging API](/reference/messaging-api/): send messages to your agent.
* [Conversation API](/reference/conversation-api/): manage the conversations these events reference.
# Amplitude
> Forward agent events to Amplitude.
The Foldspace SDK forwards agent lifecycle and interaction events to Amplitude when the Amplitude SDK is already loaded on the page. Use it to see agent activity alongside the rest of your product analytics.
Forwarding is opt-in: no events are sent until you call `enableAnalyticsForwarding`. All forwarding is wrapped in try/catch and never blocks or fails the agent.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
The Amplitude SDK must be loaded and accessible as `window.amplitude`. If it is not found, a warning is logged and no error is thrown.
## Enable forwarding
[Section titled “Enable forwarding”](#enable-forwarding)
Forward all events to Amplitude:
```javascript
const agent = foldspace.agent('YOUR-AGENT-KEY');
agent.enableAnalyticsForwarding({
providers: ["amplitude"]
});
```
Forward specific events only:
```javascript
agent.enableAnalyticsForwarding({
providers: ["amplitude"],
events: ["agent.ready", "conversation.created", "action.called"]
});
```
## enableAnalyticsForwarding(config?)
[Section titled “enableAnalyticsForwarding(config?)”](#enableanalyticsforwardingconfig)
Enables forwarding of agent events to analytics providers found on `window`.
**Parameters:**
| Parameter | Type | Required | Description |
| :-------- | :---------------------------------- | :------- | :------------------------------------------------------------------------------------------------------- |
| config | `AnalyticsForwardingConfig \| null` | No | Provider and event selection. If omitted, all supported providers and all events are enabled by default. |
**AnalyticsForwardingConfig:**
| Field | Type | Default | Description |
| :-------- | :------------------------------------------- | :------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| providers | `("mixpanel" \| "amplitude" \| "segment")[]` | `["mixpanel", "amplitude", "segment"]` | Which analytics providers to forward events to. Set to `["amplitude"]` to forward to Amplitude only. The provider must be available on `window`. |
| events | `string[]` | All events | Which events to forward. If omitted, all supported events are forwarded. Invalid event names are silently skipped. |
**Returns:** The agent instance, enabling method chaining.
## Supported events
[Section titled “Supported events”](#supported-events)
All forwarded events are prefixed with `foldspace.` (e.g. `foldspace.agent.ready`).
| Event | Description |
| :--------------------- | :----------------------------------- |
| `agent.ready` | Agent has initialized and is ready |
| `agent.removed` | Agent has been removed from the page |
| `agent.open` | Agent UI was opened |
| `agent.closed` | Agent UI was closed |
| `conversation.created` | A new conversation was started |
| `conversation.history` | An existing conversation was loaded |
| `action.called` | An action was executed |
| `action.response` | An action returned a response |
Every event payload is automatically enriched with `agentName`.
## Notes
[Section titled “Notes”](#notes)
* If called before the agent is ready, the call is queued and replayed once the agent initializes.
* Only the payload properties listed above are forwarded per event. No sensitive or unrelated data is sent.
* To forward to Mixpanel or Segment as well, add them to the providers array or see [Mixpanel](/reference/mixpanel/) and [Segment](/reference/segment/).
# Conversation
> List, search, pin, rename, delete, and select your agent's conversations.
Manage a user’s conversations with your agent: fetch and search them, pin the important ones, and rename, delete, or select a conversation by ID.
Prerequisites
The [SDK is installed and initialized](/start/install/). Call these methods inside the `foldspace('when', 'ready', ...)` callback.
## List conversations
[Section titled “List conversations”](#list-conversations)
### `getConversations(options)`
[Section titled “getConversations(options)”](#getconversationsoptions)
Fetch conversations with pagination, search, and filtering.
```javascript
foldspace.agent('AGENT_NAME').getConversations({
pagination: { pageNumber: number, pageSize: number },
search: { value: string },
filters: { pinned: boolean }
});
```
| Parameter | Type | Description |
| ----------------------- | ------- | ------------------------------------------- |
| `pagination.pageNumber` | number | Page to fetch. |
| `pagination.pageSize` | number | Number of conversations per page. |
| `search.value` | string | Filter conversations by search term. |
| `filters.pinned` | boolean | When set, restrict results by pinned state. |
## Get a conversation name
[Section titled “Get a conversation name”](#get-a-conversation-name)
### `getConversationName(conversationId)`
[Section titled “getConversationName(conversationId)”](#getconversationnameconversationid)
Get the name (title) of a conversation by ID.
```javascript
foldspace.agent('AGENT_NAME').getConversationName('CONVERSATION_ID');
```
## Pin and unpin conversations
[Section titled “Pin and unpin conversations”](#pin-and-unpin-conversations)
### `pinConversation(conversationId)` / `unpinConversation(conversationId)`
[Section titled “pinConversation(conversationId) / unpinConversation(conversationId)”](#pinconversationconversationid--unpinconversationconversationid)
Pin or unpin a conversation by ID.
```javascript
foldspace.agent('AGENT_NAME').pinConversation('CONVERSATION_ID');
foldspace.agent('AGENT_NAME').unpinConversation('CONVERSATION_ID');
```
## Rename and delete conversations
[Section titled “Rename and delete conversations”](#rename-and-delete-conversations)
### `renameConversation(conversationId, name)` / `deleteConversation(conversationId)`
[Section titled “renameConversation(conversationId, name) / deleteConversation(conversationId)”](#renameconversationconversationid-name--deleteconversationconversationid)
Rename or delete a conversation by ID.
```javascript
foldspace.agent('AGENT_NAME').deleteConversation('CONVERSATION_ID');
foldspace.agent('AGENT_NAME').renameConversation('CONVERSATION_ID', 'NEW_NAME');
```
## Select a conversation
[Section titled “Select a conversation”](#select-a-conversation)
### `selectConversation(conversationId)`
[Section titled “selectConversation(conversationId)”](#selectconversationconversationid)
Make a conversation the active one by ID.
```javascript
foldspace.agent('AGENT_NAME').selectConversation('CONVERSATION_ID');
```
## Related
[Section titled “Related”](#related)
* [Messaging API](/reference/messaging-api/): send messages to your agent.
* [Agent Event API](/reference/agent-event-api/): react to `conversation.created` and other events.
# Fuzzy search
> Fuzzy-search a local list with agent.searchInList() to resolve loose user phrasing to a concrete record before executing an action.
`searchInList()` fuzzy-matches a query against a local array, with no network call. Use it inside action handlers to resolve a user’s loose phrasing (“the acme account”, “jon from billing”) to a concrete record before you build action parameters or feed data to a [Task Agent](/reference/task-agent-api/).
Prerequisites
The [SDK is installed and initialized](/start/install/). Call `searchInList()` on an agent instance, typically inside an action’s `execute` handler.
## Why fuzzy matching
[Section titled “Why fuzzy matching”](#why-fuzzy-matching)
In a conversational interface, users refer to records the way they remember them, not the way your database stores them. They type “acme” when the account is named “ACME Corporation Ltd”, “jon smth” when the user is “Jon Smith”, and they misspell both. An exact or substring lookup returns nothing for any of these, which forces the agent to come back with “no results” for a record that plainly exists.
Fuzzy matching closes that gap: it compares the query approximately, tolerating typos, partial words, and missing pieces. Each candidate gets a relevance `score` between `0` (perfect match) and `1` (barely related), and `searchInList()` drops anything weaker than `0.4` and returns the rest sorted best-first. Your handler then decides what to do with the confidence it has: auto-select a clear winner, or return the top candidates so the agent can ask the user to pick.
## Signature
[Section titled “Signature”](#signature)
```typescript
searchInList(
items: T[],
query: string,
options: { searchKeys: string[] },
): { item: T; refIndex: number; score?: number }[];
```
| Parameter | Type | Description |
| :------------------- | :--------- | :----------------------------------------------------------------------- |
| `items` | `T[]` | The list to search. Objects or primitives. |
| `query` | `string` | The search string, usually a user-supplied name or term. |
| `options.searchKeys` | `string[]` | Property paths to match against each item, for example `["name", "id"]`. |
## Return value
[Section titled “Return value”](#return-value)
An array of matches, sorted most-relevant first:
| Key | Type | Description |
| :--------- | :------- | :--------------------------------------------------------------------------------- |
| `item` | `T` | The matched element from `items`. |
| `refIndex` | `number` | Its index in the original `items` array. |
| `score` | `number` | Relevance. `0` is a perfect match; lower is better. Present when a query is given. |
Edge cases:
* An empty or whitespace-only `query` returns every item in original order, with no `score`.
* Empty `items` or empty `searchKeys` returns `[]`.
* Matches weaker than the built-in `0.4` threshold are dropped.
## Resolve an account
[Section titled “Resolve an account”](#resolve-an-account)
Match against every field a user might reference: name, domain, or ID in one call. Here “acme” resolves to the ACME Corporation record even though the user never typed the full name:
lookup-account.js
```javascript
foldspace("when", "ready", () => {
const agent = foldspace.agent({ /* …common setup… */ });
const accounts = [
{ id: "acc-001", name: "ACME Corporation Ltd", domain: "acme.com", plan: "Enterprise" },
{ id: "acc-002", name: "Acmar Logistics", domain: "acmar.io", plan: "Pro" },
{ id: "acc-003", name: "Northwind Traders", domain: "northwind.com", plan: "Pro" },
];
agent.addActionHandlers({
lookup_account: {
execute: async (params) => {
const matches = agent
.searchInList(accounts, params.query, { searchKeys: ["name", "domain", "id"] })
.filter((r) => r.score < 0.4);
if (matches.length === 0) {
return { found: false, message: "No matching account." };
}
// A single confident hit: hand the agent the resolved record.
if (matches.length === 1 || matches[0].score < 0.2) {
return { found: true, account: matches[0].item };
}
// Ambiguous: return the candidates so the agent asks the user to pick.
return { found: true, candidates: matches.map((r) => r.item) };
},
},
});
});
```
## Resolve a user
[Section titled “Resolve a user”](#resolve-a-user)
The same pattern resolves people. “jon smth” matches “Jon Smith” despite the missing letters, and searching `email` too lets “jon@” or a partial address work:
```javascript
const matches = agent
.searchInList(users, params.query, { searchKeys: ["name", "email"] })
.filter((r) => r.score < 0.4)
.map((r) => r.item);
const user = matches[0]; // best match, or undefined if nothing cleared the bar
```
## Try exact matches first
[Section titled “Try exact matches first”](#try-exact-matches-first)
Cheap exact or substring checks first, fuzzy `searchInList()` as the fallback. It is faster and avoids surprising matches when the user pasted an exact ID or email:
```javascript
const exact = users.find(
(u) => u.email === query || u.name === query,
);
const match =
exact ??
agent
?.searchInList?.(users, query, { searchKeys: ["name", "email"] })
.filter((r) => r.score < 0.4)[0]?.item;
```
Optional-chain the call (`agent?.searchInList?.(...)`) when the agent may not be ready in the calling context.
## Pick a score cutoff
[Section titled “Pick a score cutoff”](#pick-a-score-cutoff)
The score is your confidence dial. Two thresholds cover most actions:
* `< 0.4`: “plausible”. Use it to build a candidate list and let the agent confirm with the user.
* `< 0.2`: “confident”. Use it to auto-select a single record without a confirmation round-trip, for example when “Q1 creators” should silently resolve to the “Q1 Creators 2026” list.
Tip
Prefer the looser cutoff plus a confirmation step for destructive or high-stakes actions (deleting, billing, messaging someone). Save auto-select for lookups where a near-miss is cheap to undo.
## Related
[Section titled “Related”](#related)
* [Task Agent API](/reference/task-agent-api/): feed resolved records into a background task.
* [Execute actions](/guides/executing-actions/): write the `execute` handler this API is called from.
* [SDK APIs](/reference/sdk-apis/): the full programmatic surface of the agent instance.
# MCP server integration
> Connect your agent to external tools and services over the Model Context Protocol (MCP).
Connect your agent to external tools, data sources, and services using the Model Context Protocol (MCP). MCP is an open standard, developed by Anthropic, that standardizes how AI applications talk to external systems: a universal adapter that lets your agent interact with anything that speaks MCP.
## Connect a server
[Section titled “Connect a server”](#connect-a-server)
1. Navigate to **Agent Studio → select your agent → MCP Servers** tab.
2. Click **Add Server**.
3. Enter the server details.
4. Click **Test Connection** to verify connectivity.
5. Review the available tools, then click **Save Server**.
Your agent can now use the tools provided by your MCP server.
## Configuration reference
[Section titled “Configuration reference”](#configuration-reference)
The core settings required to connect to an MCP server.
### Display name
[Section titled “Display name”](#display-name)
A friendly name to identify this server in your dashboard.
```text
Example: "Customer Database" or "Logistics API"
```
### Base URL
[Section titled “Base URL”](#base-url)
The root URL of your MCP server.
```text
Example: https://mcp.yourcompany.com
```
Must be a valid URL with an `http://` or `https://` protocol. Use HTTPS in production.
### Connection type
[Section titled “Connection type”](#connection-type)
Choose how your agent communicates with the MCP server.
| Type | Description | When to use |
| ----------------------------- | -------------------------------------------------- | ------------------------------------------------------ |
| Streamable HTTP (Recommended) | Modern HTTP-based transport with streaming support | Default choice for most integrations |
| SSE (Legacy) | Server-Sent Events transport | Only if your MCP server doesn’t support HTTP transport |
### Endpoint path
[Section titled “Endpoint path”](#endpoint-path)
The path on the server where MCP requests are sent.
| Connection Type | Default Path |
| --------------- | ------------ |
| Streamable HTTP | `/mcp` |
| SSE | `/sse` |
Tip
Most MCP servers use the default paths. Only change this if your server uses a custom endpoint.
### API key / secret token
[Section titled “API key / secret token”](#api-key--secret-token)
Authentication token for your MCP server, if required.
```text
Example: sk-xxxxxxxxxxxxxxxxxxxx
```
Tokens are encrypted at rest and never displayed in the UI after saving. To update a configuration without changing the token, leave this field empty.
## Advanced settings
[Section titled “Advanced settings”](#advanced-settings)
These optional settings fine-tune your integration.
### Forward user context
[Section titled “Forward user context”](#forward-user-context)
When enabled, Foldspace automatically includes the current user’s identity in requests to your MCP server via the `X-Foldspace-User-Id` header.
| Setting | Behavior |
| -------- | ------------------------------------------------- |
| Enabled | User’s identity ID is sent with every MCP request |
| Disabled | No user context is forwarded (default) |
Use this when:
* Your MCP server needs to personalize responses per user.
* You want to implement user-level access controls on your server.
* You need to audit which user triggered which action.
Example headers:
```text
X-Foldspace-User-Id: user_abc123
X-Foldspace-Subscription-Id: sub_xyz789
```
### Custom headers
[Section titled “Custom headers”](#custom-headers)
Add static headers to every request sent to your MCP server.
| Use Case | Header Example |
| -------------- | --------------------------- |
| API Versioning | `X-API-Version: 2024-01` |
| Routing | `X-Environment: production` |
## Test your integration
[Section titled “Test your integration”](#test-your-integration)
Test the connection before saving to confirm everything works.
### Test the connection
[Section titled “Test the connection”](#test-the-connection)
The **Test Connection** button performs a full MCP initialization handshake with your server.
| Result | Meaning |
| ------- | -------------------------------------------------------------- |
| Success | Server is reachable and responds correctly to the MCP protocol |
| Failed | Connection issue, check the error message for details |
Common error messages:
| Error | Cause | Solution |
| ------------------ | ---------------------------------- | ------------------------------------------------------- |
| Connection refused | Server not running or wrong URL | Verify the server is running and the URL is correct |
| Connection timeout | Network issue or firewall blocking | Check network access and firewall rules |
| 401 Unauthorized | Invalid or missing API key | Verify your API key is correct |
| 404 Not Found | Wrong endpoint path | Check the endpoint path configuration |
| Protocol error | Server doesn’t speak MCP | Ensure the server implements the MCP protocol correctly |
### View available tools
[Section titled “View available tools”](#view-available-tools)
After a successful connection test, click **View Available Tools** to see what your MCP server provides. Each tool displays:
* **Name**: The identifier your agent uses to invoke the tool.
* **Description**: What the tool does (shown to the AI).
* **Input Schema**: The parameters the tool accepts.
Tip
Review the tool list to confirm you’ve connected to the correct server and the expected tools are available.
## Limits and quotas
[Section titled “Limits and quotas”](#limits-and-quotas)
| Resource | Limit |
| --------------------- | ------------------------ |
| MCP servers per agent | 20 |
| Display name | Must be unique per agent |
| Base URL | Valid HTTP/HTTPS URL |
| Custom headers | Recommended max 10 |
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
### My agent isn’t using the MCP tools
[Section titled “My agent isn’t using the MCP tools”](#my-agent-isnt-using-the-mcp-tools)
1. **Verify the server is enabled.** Disabled servers don’t sync to the agent.
2. **Check tool descriptions.** The AI uses descriptions to decide when to call tools: make them clear and specific.
3. **Test the connection.** Ensure the server is reachable.
### Connection test passes but tools aren’t working
[Section titled “Connection test passes but tools aren’t working”](#connection-test-passes-but-tools-arent-working)
1. **Verify the input schema.** Ensure the AI is sending parameters in the expected format.
2. **Review server logs.** Check your MCP server’s logs for errors.
3. **Test tools directly.** Use the “List Tools” endpoint to verify tool availability.
## FAQ
[Section titled “FAQ”](#faq)
**Can I connect multiple MCP servers to one agent?** Yes. You can connect up to 20 MCP servers to a single agent. Each server’s tools are available to the agent, which chooses the appropriate tool based on the conversation context.
**Can I use MCP servers behind a VPN?** Currently, MCP servers must be accessible via the public internet. For private networks, use a secure tunnel or API gateway that’s publicly accessible with proper authentication.
## Get help
[Section titled “Get help”](#get-help)
* Email .
* Use the in-app support widget.
# Messaging
> Send chat messages to your agent as if the user typed them.
Send messages to your agent as if the user typed them, blending conversation, actions, and UI components into your product flows.
Every call references your Agent API Name, found in **Agent Studio → Setup**.
## Initialize the agent
[Section titled “Initialize the agent”](#initialize-the-agent)
Call agent methods once the SDK is ready.
```javascript
foldspace("when", "ready", () => {
const agent = foldspace.agent({ /* …common setup… */ });
// Now you can call any of the methods below:
agent.message("What actions can you do?");
});
```
## Send a message
[Section titled “Send a message”](#send-a-message)
### `message(text: string): this`
[Section titled “message(text: string): this”](#messagetext-string-this)
Send a chat message as if the user typed it into the agent. Returns the agent instance for chaining.
| Parameter | Type | Description |
| --------- | ------ | ---------------------------- |
| `text` | string | The message content to send. |
```javascript
foldspace.agent("myproduct-agentic-agent")
.message("Configure brand settings for me");
```
### Interpolate runtime values
[Section titled “Interpolate runtime values”](#interpolate-runtime-values)
Because `text` is a plain string, you can interpolate runtime values and chain other methods such as `.open()`.
```javascript
const userName = "John";
agent("myproduct-agentic-agent")
.message(`Hi ${userName}, let's review your pipeline today`)
.open();
```
## Related
[Section titled “Related”](#related)
* [Conversation API](/reference/conversation-api/): list, search, pin, and manage conversations.
* [Agent Event API](/reference/agent-event-api/): listen to agent, UI, and conversation events.
# Mixpanel
> Forward agent events to Mixpanel.
The Foldspace SDK forwards agent lifecycle and interaction events to Mixpanel when the Mixpanel SDK is already loaded on the page. Use it to see agent activity alongside the rest of your product analytics.
Forwarding is opt-in: no events are sent until you call `enableAnalyticsForwarding`. All forwarding is wrapped in try/catch and never blocks or fails the agent.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
The Mixpanel SDK must be loaded and accessible as `window.mixpanel`. If it is not found, a warning is logged and no error is thrown.
## Enable forwarding
[Section titled “Enable forwarding”](#enable-forwarding)
Forward all events to Mixpanel:
```javascript
const agent = foldspace.agent('YOUR-AGENT-KEY');
agent.enableAnalyticsForwarding({
providers: ["mixpanel"]
});
```
Forward specific events only:
```javascript
agent.enableAnalyticsForwarding({
providers: ["mixpanel"],
events: ["agent.ready", "conversation.created", "action.called"]
});
```
## enableAnalyticsForwarding(config?)
[Section titled “enableAnalyticsForwarding(config?)”](#enableanalyticsforwardingconfig)
Enables forwarding of agent events to analytics providers found on `window`.
**Parameters:**
| Parameter | Type | Required | Description |
| :-------- | :---------------------------------- | :------- | :------------------------------------------------------------------------------------------------------- |
| config | `AnalyticsForwardingConfig \| null` | No | Provider and event selection. If omitted, all supported providers and all events are enabled by default. |
**AnalyticsForwardingConfig:**
| Field | Type | Default | Description |
| :-------- | :------------------------------------------- | :------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- |
| providers | `("mixpanel" \| "amplitude" \| "segment")[]` | `["mixpanel", "amplitude", "segment"]` | Which analytics providers to forward events to. Set to `["mixpanel"]` to forward to Mixpanel only. The provider must be available on `window`. |
| events | `string[]` | All events | Which events to forward. If omitted, all supported events are forwarded. Invalid event names are silently skipped. |
**Returns:** The agent instance, enabling method chaining.
## Supported events
[Section titled “Supported events”](#supported-events)
All forwarded events are prefixed with `foldspace.` (e.g. `foldspace.agent.ready`).
| Event | Description |
| :--------------------- | :----------------------------------- |
| `agent.ready` | Agent has initialized and is ready |
| `agent.removed` | Agent has been removed from the page |
| `agent.open` | Agent UI was opened |
| `agent.closed` | Agent UI was closed |
| `conversation.created` | A new conversation was started |
| `conversation.history` | An existing conversation was loaded |
| `action.called` | An action was executed |
| `action.response` | An action returned a response |
Every event payload is automatically enriched with `agentName`.
## Notes
[Section titled “Notes”](#notes)
* If called before the agent is ready, the call is queued and replayed once the agent initializes.
* Only the payload properties listed above are forwarded per event. No sensitive or unrelated data is sent.
* To forward to Amplitude or Segment as well, add them to the providers array or see [Amplitude](/reference/amplitude/) and [Segment](/reference/segment/).
# Public API
> Generate and manage the REST API keys that authenticate programmatic access to your Foldspace account.
The Foldspace REST API lets you integrate directly with the platform. As an **admin**, you create and manage the API keys that authenticate those requests.
## Access API key management
[Section titled “Access API key management”](#access-api-key-management)
1. Open the **Apps** dropdown at the top-left, under **Agent Studio**.
2. Go to **Settings → Public API**.
You’ll see a list of existing API keys with their names and descriptions.
## Generate a new API key
[Section titled “Generate a new API key”](#generate-a-new-api-key)
1. Click **Create**.
2. Fill out the form:
* **Name**: A short, clear name for the key, such as `Vibe Coding`, `Test Prod`, or `LocalTesting`.
* **Description**: Optional. Explain what the key is used for, such as `Key for testing API calls in staging`.
* **Expiry Date**: Optional. Add an expiration for tighter security.
3. Click **Generate**.
Caution
The new key is shown **only once**. Copy it and store it securely before leaving the page.
## Related
[Section titled “Related”](#related)
* [Authentication](/start/authentication/): how Foldspace authenticates requests.
* [API Reference, Overview](/reference/overview/): base URL, versioning, and conventions.
# SDK APIs
> The SDK's programmatic surface — send messages, read conversation and agent state, control visibility, and send events.
The SDK exposes a small set of methods on an agent instance. Call them once the SDK is ready:
```javascript
foldspace("when", "ready", () => {
const agent = foldspace.agent({ /* setup */ });
// call any SDK API below
});
```
## The APIs
[Section titled “The APIs”](#the-apis)
* **[Messaging](/reference/messaging-api/)** — send chat messages as if the user typed them.
* **[Conversation](/reference/conversation-api/)** — read and control the current conversation.
* **[Agent Event](/reference/agent-event-api/)** — subscribe to agent lifecycle and interaction events.
* **[Visibility](/reference/visibility-api/)** — open, close, and show or hide the agent.
* **[Fuzzy search](/reference/fuzzy-search-api/)** — fuzzy-match user phrasing to a record in a local list.
* **[Custom events](/reference/track-api/)** — send your own product events with `foldspace.track()`.
* **[Test mode](/reference/test-mode/)** — run the agent in test mode without polluting real analytics.
## Where to start
[Section titled “Where to start”](#where-to-start)
New to the SDK? [Set up](/start/install/) the agent first, then [Messaging](/reference/messaging-api/) is the simplest call to try. To react to what the agent does, use [Agent Event](/reference/agent-event-api/).
# Segment
> Forward agent events to Segment.
The Foldspace SDK forwards agent lifecycle and interaction events to [Segment](https://segment.com) when the Segment analytics.js library is already loaded on the page. Segment then fans those events out to every destination you have connected, so agent activity flows into the rest of your product analytics.
Forwarding is opt-in: no events are sent until you call `enableAnalyticsForwarding`. All forwarding is wrapped in try/catch and never blocks or fails the agent.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
The Segment analytics.js library must be loaded and accessible as `window.analytics`. If it is not found, a warning is logged and no error is thrown.
## Enable forwarding
[Section titled “Enable forwarding”](#enable-forwarding)
Forward all events to Segment:
```javascript
const agent = foldspace.agent('YOUR-AGENT-KEY');
agent.enableAnalyticsForwarding({
providers: ["segment"]
});
```
Forward specific events only:
```javascript
agent.enableAnalyticsForwarding({
providers: ["segment"],
events: ["agent.ready", "conversation.created", "action.called"]
});
```
Each event is sent with `analytics.track()`, named with the `foldspace.` prefix (e.g. `analytics.track("foldspace.conversation.created", { agentName })`).
## enableAnalyticsForwarding(config?)
[Section titled “enableAnalyticsForwarding(config?)”](#enableanalyticsforwardingconfig)
Enables forwarding of agent events to analytics providers found on `window`.
**Parameters:**
| Parameter | Type | Required | Description |
| :-------- | :---------------------------------- | :------- | :------------------------------------------------------------------------------------------------------- |
| config | `AnalyticsForwardingConfig \| null` | No | Provider and event selection. If omitted, all supported providers and all events are enabled by default. |
**AnalyticsForwardingConfig:**
| Field | Type | Default | Description |
| :-------- | :------------------------------------------- | :------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
| providers | `("mixpanel" \| "amplitude" \| "segment")[]` | `["mixpanel", "amplitude", "segment"]` | Which analytics providers to forward events to. Set to `["segment"]` to forward to Segment only. The provider must be available on `window`. |
| events | `string[]` | All events | Which events to forward. If omitted, all supported events are forwarded. Invalid event names are silently skipped. |
**Returns:** The agent instance, enabling method chaining.
## Supported events
[Section titled “Supported events”](#supported-events)
All forwarded events are prefixed with `foldspace.` (e.g. `foldspace.agent.ready`).
| Event | Description |
| :--------------------- | :----------------------------------- |
| `agent.ready` | Agent has initialized and is ready |
| `agent.removed` | Agent has been removed from the page |
| `agent.open` | Agent UI was opened |
| `agent.closed` | Agent UI was closed |
| `conversation.created` | A new conversation was started |
| `conversation.history` | An existing conversation was loaded |
| `action.called` | An action was executed |
| `action.response` | An action returned a response |
Every event payload is automatically enriched with `agentName`.
## Notes
[Section titled “Notes”](#notes)
* If called before the agent is ready, the call is queued and replayed once the agent initializes.
* Only the payload properties listed above are forwarded per event. No sensitive or unrelated data is sent.
* To forward to other providers as well, add them to the providers array or see [Amplitude](/reference/amplitude/) and [Mixpanel](/reference/mixpanel/).
# Task Agent
> Run curated LLM tasks from code and get trusted text or JSON back, in complete or streaming mode.
Run a configured task through the SDK and get back text or JSON you can trust, adding AI to a product feature without writing prompts: call a curated, versioned, observable skill from code.
Reach for it when you need:
* **Synchronous AI APIs**: quick, reliable responses inside a product feature.
* **Deterministic contracts**: predictable outputs shaped by a schema.
* **Non-chat tasks**: summaries, classification, extraction, or generation.
Both complete and streaming modes use the same call: `foldspace.agent('YOUR-AGENT-API-NAME').runTask()`. Passing `streamOptions` switches it to streaming.
## Parameters
[Section titled “Parameters”](#parameters)
| Parameter | Type | Required | Description |
| :-------------- | :------------------- | :------- | :----------------------------------------------------------------------------------------------- |
| `taskKey` | `string` | Yes | The unique identifier for the task you want to run. |
| `data` | `string` \| `object` | Yes | The input payload for the task. |
| `streamOptions` | `object` | No | Enables streaming mode if provided. |
| `cacheOptions` | `object` | No | Configures caching behavior, such as defining a Time-To-Live (TTL) or forcing a fresh execution. |
## Complete mode
[Section titled “Complete mode”](#complete-mode)
Call `runTask()` without `streamOptions` to wait for the server and resolve a Promise with the full result once the task finishes.
```javascript
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'YOUR-TASK-KEY',
data: 'ANY-STRING-OR-OBJECT'
})
.then((result) => {
console.log('Task result:', result);
})
.catch((error) => {
console.error('Task failed:', error);
});
```
The resolved value depends on the task’s configured response type:
* **`string`** for `TEXT` response types.
* **`object`** for `JSON` response types.
## Cache options
[Section titled “Cache options”](#cache-options)
The optional `cacheOptions` object controls when cached results are used, when to force a fresh execution, and how long new results are cached. Caching behaves identically in both complete and streaming modes.
| Property | Type | Default | Description |
| :----------- | :-------- | :---------------- | :---------------------------------------------------------------------------------------------- |
| `bypass` | `boolean` | `false` | When `true`, the task executes fresh, ignoring any existing cached result. |
| `ttlSeconds` | `number` | `21600` (6 hours) | How long to cache the new result, in seconds. Set to `0` to disable caching for the new result. |
### Behavior matrix
[Section titled “Behavior matrix”](#behavior-matrix)
| `bypass` | `ttlSeconds` | Result |
| :---------------- | :------------- | :------------------------------------------------------------ |
| Omitted / `false` | Omitted | Check cache. If executed, cache new result for 6 hours. |
| Omitted / `false` | `300` (custom) | Check cache. If executed, cache new result for 5 minutes. |
| Omitted / `false` | `0` | Check cache. If executed, do not cache new result. |
| `true` | Omitted | **Skip cache**. Execute fresh & cache new result for 6 hours. |
| `true` | `0` | **Skip cache**. Execute fresh & cache new result for 6 hours. |
### Force a refresh
[Section titled “Force a refresh”](#force-a-refresh)
Ignore any cached value and run the task. The new result is cached for the default 6 hours.
```javascript
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'SUMMARIZE_TASK',
data: 'some long text...',
cacheOptions: {
bypass: true
}
});
```
### Skip caching the new result
[Section titled “Skip caching the new result”](#skip-caching-the-new-result)
Still check for an existing cached result, but if a new result is generated (for example, because no cache was found), don’t store it for future use.
```javascript
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'SUMMARIZE_TASK',
data: 'some long text...',
cacheOptions: {
ttlSeconds: 0
}
});
```
### Full-fresh, no-cache
[Section titled “Full-fresh, no-cache”](#full-fresh-no-cache)
Skip reading from the cache and prevent the new result from being stored.
```javascript
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'SUMMARIZE_TASK',
data: 'some long text...',
cacheOptions: {
bypass: true,
ttlSeconds: 0
}
});
```
### Custom TTL with streaming
[Section titled “Custom TTL with streaming”](#custom-ttl-with-streaming)
`cacheOptions` works the same in streaming mode. This caches the streamed result for 10 minutes (600 seconds).
```javascript
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'LONG_STREAM_TASK',
data: { prompt: 'Generate a story...' },
// Cache this specific stream's result for 10 minutes
cacheOptions: {
ttlSeconds: 600
},
streamOptions: {
onMessage: ({ textDelta }) => {
// Append text to UI
},
onComplete: () => {
console.log('Stream done and result is cached.');
}
}
});
```
## Streaming mode
[Section titled “Streaming mode”](#streaming-mode)
Provide a `streamOptions` object to receive incremental chunks as they become available, ideal for AI text generation, incremental updates, or long-running processes. Each chunk triggers `onMessage`, letting you render output progressively.
Note
Streaming mode is only supported for tasks configured with a `TEXT` response type. It is not compatible with `JSON` response types.
```javascript
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'YOUR-TASK-KEY',
data: { input: "Hello, world" },
streamOptions: {
onStart: ({ id, abortStreamTask }) => {
console.log('Stream started with ID:', id);
// You can call abortStreamTask() to stop the stream at any time
},
onMessage: ({ textDelta, fullText, chunk }) => {
console.log('New message chunk:', textDelta);
console.log('Full accumulated text:', fullText);
},
onComplete: ({ fullText }) => {
console.log('Stream completed:', fullText);
},
onError: (error) => {
console.error('Stream error:', error);
}
}
});
```
### `streamOptions` callbacks
[Section titled “streamOptions callbacks”](#streamoptions-callbacks)
| Callback | Description | Parameters |
| :----------- | :---------------------------------------------------- | :---------------------------------------------------------------------- |
| `onStart` | Called when the stream begins. | `{ id: string, abortStreamTask: () => void }` |
| `onMessage` | Called for every data chunk received from the stream. | `{ textDelta: string, fullText: string, chunk: object, id: string }` |
| `onComplete` | Called once the stream ends (naturally or via abort). | `{ textDelta?: string, fullText: string, chunk?: object, id?: string }` |
| `onError` | Called if an error occurs during the stream. | `(error: Error)` |
### Aborting a stream manually
[Section titled “Aborting a stream manually”](#aborting-a-stream-manually)
The `onStart` callback provides an `abortStreamTask` function. Store it and call it at any time to stop the stream from the client side, useful for “Stop Generation” buttons, handling user navigation, or enforcing a client-side timeout. Calling `abortStreamTask()` gracefully terminates the stream, and `onComplete` still fires.
```javascript
// Store the abort function in a higher scope
let abortTask = null;
function startStream() {
foldspace.agent('YOUR-AGENT-API-NAME').runTask({
taskKey: 'LONG_STREAM_TASK',
data: { prompt: 'Generate a very long story...' },
streamOptions: {
onStart: ({ id, abortStreamTask }) => {
console.log('Stream started:', id);
// Store the function so it can be called by other elements
abortTask = abortStreamTask;
},
onMessage: ({ textDelta }) => {
console.log(textDelta); // Append text to UI
},
onComplete: ({ fullText }) => {
console.log('Stream completed.');
abortTask = null; // Clean up
},
onError: (err) => {
console.error('Stream error:', err);
abortTask = null; // Clean up
},
}
});
}
// Imagine this is called by a "Stop" button click
function stopStream() {
if (abortTask) {
console.log('Manually aborting stream...');
abortTask();
}
}
```
## Notes
[Section titled “Notes”](#notes)
* Run any registered agent API using its unique `taskKey`. Create and manage your task agents in **Agent Studio → Task Agents** (see the [Task Agents guide](/user-guides/task-agents/)).
* For long tasks, prefer streaming mode to get progressive feedback.
* Both modes use `foldspace.agent('YOUR-AGENT-API-NAME').runTask()`: behavior changes based on whether `streamOptions` is provided.
# Test mode
> Test your agent against your live production site without polluting analytics, conversations, or reports with your own test traffic.
Test mode lets you talk to your agent on your real production site while keeping the conversation out of your data. Sessions started in test mode are flagged as test traffic, so they never count toward your analytics, and they stay out of the default Conversations list. Use it to smoke-test a new action, a knowledge update, or a copy change against production without your own messages skewing reports.
Turn it on from the browser console with `setTestMode`. No redeploy, no separate staging agent.
Prerequisites
The [SDK is installed](/start/install/) on your production site.
## Enable test mode
[Section titled “Enable test mode”](#enable-test-mode)
Open your production site, open the browser console, and run:
```javascript
foldspace("when", "ready", () => {
foldspace.agent({
apiName: "YOUR-AGENT-KEY",
}).setTestMode(true);
});
```
Replace `YOUR-AGENT-KEY` with your Agent API Name from **Agent Studio → Setup**. Once it’s on, the agent shows a striped banner so you always know the current session is being excluded from your data:
Every conversation you start while the banner is showing is tagged as a test session.
## Turn it off
[Section titled “Turn it off”](#turn-it-off)
Test mode is scoped to the current page session. Reload the page to clear it, or turn it off explicitly:
```javascript
foldspace.agent("YOUR-AGENT-KEY").setTestMode(false);
```
## `setTestMode(enabled)`
[Section titled “setTestMode(enabled)”](#settestmodeenabled)
| Parameter | Type | Required | Description |
| --------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled` | boolean | Yes | `true` flags all new sessions on this page as test traffic and shows the banner. `false` returns the agent to normal tracked behavior. |
## See your test conversations
[Section titled “See your test conversations”](#see-your-test-conversations)
Test sessions are hidden from **Agent Studio → Conversations** by default so real user activity stays clean. To surface them, filter by source:
1. In the Conversations filter bar, open the **Source** filter. If it isn’t showing, add it from **+ Add filter**.
2. Check **SDK Test** (test-mode sessions are recorded under this source), then **Apply**.
The list now shows your test conversations. Open one to check the [timeline and View Analysis](/user-guides/conversations/) and confirm the agent behaved as expected. Leave **Live Agent** unchecked to see test traffic on its own.
Note
Test sessions are excluded from analytics whether or not you filter for them. The **Source** filter only controls what you *see* in the Conversations list; it doesn’t change what gets counted.
Caution
Test mode only affects the browser session where you called `setTestMode(true)`. It does not put your production agent into test mode for other visitors, and it is not a way to hide the agent from real users.
## Related
[Section titled “Related”](#related)
* [Conversations](/user-guides/conversations/): review and filter sessions, including test traffic.
* [Custom events](/reference/track-api/): send your own product events into analytics.
* [Visibility](/reference/visibility-api/): show, hide, open, or close the agent from code.
# Custom Events
> Send your own product events into Foldspace analytics with foldspace.track() to power audiences, segments, and reports.
Record custom events from your product with `foldspace.track()`. Each event is attributed to the current identified user and flows into Foldspace analytics — every event shows up in the [Event Explorer](/user-guides/analytics/events-metrics/), and the things people do in your app (not just their agent conversations) become available across the [Audience](/user-guides/analytics/audiences/), [Segments](/user-guides/analytics/segments/), and [Reports](/user-guides/analytics/reports/).
This is the inbound counterpart to analytics forwarding: forwarding pushes agent events *out* to [Mixpanel](/reference/mixpanel/) or [Amplitude](/reference/amplitude/), while `track()` brings your product events *in* to Foldspace.
Prerequisites
The [SDK is installed](/start/install/) and you’ve [identified the user](/start/user-context/) — each event is attributed to the identified user.
## `track(eventName, properties?)`
[Section titled “track(eventName, properties?)”](#trackeventname-properties)
```javascript
foldspace.track("Report_Exported", {
report_type: "quarterly_summary",
format: "pdf",
page_count: 12,
workspace: "acme-inc",
});
```
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `eventName` | string | Yes | The name of the event, e.g. `Report_Exported`. Use a consistent, stable name so events group correctly in analytics. |
| `properties` | object | No | A flat map of custom attributes describing the event. Values should be strings, numbers, or booleans. |
## Naming events and properties
[Section titled “Naming events and properties”](#naming-events-and-properties)
* Keep event names **stable and human-readable** — `Report_Exported`, `Checkout_Completed`, `Plan_Upgraded`. The same name should always represent the same action.
* Use `snake_case` keys for properties and keep them **flat** (no nested objects or arrays). Flat string/number/boolean values are what segments and report filters can group and filter on.
* Send the same property keys every time you fire a given event, so values line up across users.
## Attributing events to a user
[Section titled “Attributing events to a user”](#attributing-events-to-a-user)
Events are attached to the user Foldspace currently knows about. To make sure events land on the right person, identify your user first via [User context](/start/user-context/):
```javascript
// Identify the signed-in user once, early in the page lifecycle.
foldspace.agent("AGENT_NAME").setUser({
id: "user_123",
email: "ada@example.com",
});
// Later, anywhere in your app, record what they do.
foldspace.track("Plan_Upgraded", {
from_plan: "free",
to_plan: "pro",
seats: 5,
});
```
If no user has been identified yet, the event is still recorded and associated with the current anonymous visitor.
## Examples
[Section titled “Examples”](#examples)
Track a simple event with no properties:
```javascript
foldspace.track("Pricing_Page_Viewed");
```
Track a conversion with structured attributes:
```javascript
foldspace.track("Checkout_Completed", {
order_id: "ord_8821",
currency: "USD",
amount: 129.0,
items: 3,
coupon_applied: true,
});
```
## Notes
[Section titled “Notes”](#notes)
* Call `track()` any time after the embed snippet has loaded on the page.
* Only the properties you pass are sent — Foldspace does not collect additional page data with the event.
* Custom events appear in the [Event Explorer](/user-guides/analytics/events-metrics/) and alongside agent activity in a user’s [profile](/user-guides/analytics/user-profiles/), and can be used to build [segments](/user-guides/analytics/segments/) and [reports](/user-guides/analytics/reports/).
## Related
[Section titled “Related”](#related)
* [Event Explorer](/user-guides/analytics/events-metrics/): browse and verify the events you send.
* [User context](/start/user-context/): identify users so events attribute correctly.
* [Mixpanel](/reference/mixpanel/) and [Amplitude](/reference/amplitude/): forward agent events out to your analytics stack.
* [Audience](/user-guides/analytics/audiences/): see every identified and tracked user.
# Visibility
> Programmatically show, hide, open, close, or remove your Foldspace agent for full UI control.
Control your agent’s UI from code: open or close the prompt, show or hide the widget, or tear it down entirely. Use it to wire the agent into your app’s own controls and lifecycle.
Every call references your Agent API Name, found in **Agent Studio → Setup**.
Prerequisites
The [SDK is installed and initialized](/start/install/). Call these methods inside the `foldspace('when', 'ready', ...)` callback.
## Initialize the agent
[Section titled “Initialize the agent”](#initialize-the-agent)
```javascript
foldspace("when", "ready", () => {
const agent = foldspace.agent({ /* …common setup… */ });
// Now you can call any of the methods below:
// agent.message(...).open().setViewType("WIDGET").hide()...
});
```
## Open and close
[Section titled “Open and close”](#open-and-close)
Open the agent prompt:
```javascript
foldspace.agent("myproduct-agentic-agent").open();
```
Close the agent prompt:
```javascript
foldspace.agent("myproduct-agentic-agent").close();
```
## Show and hide
[Section titled “Show and hide”](#show-and-hide)
Show the agent widget:
```javascript
foldspace.agent("myproduct-agentic-agent").show();
```
Hide the agent widget:
```javascript
foldspace.agent("myproduct-agentic-agent").hide();
```
## Remove
[Section titled “Remove”](#remove)
Tear down the agent completely: unmounts the UI, clears its state, and frees resources.
```javascript
agent("myproduct-agentic-agent").remove();
```
# Domain allow list
> Firewall rules required for the Foldspace SDK to function in restricted network environments.
When deploying the Foldspace SDK, some corporate firewalls may block external URLs by default. To ensure the SDK functions correctly, you may need to allow-list specific domains.
## When is an Allow List Required?
[Section titled “When is an Allow List Required?”](#when-is-an-allow-list-required)
If your environment restricts outbound traffic or blocks unknown URLs, adding the Foldspace service domains to your firewall ensures:
* The SDK can load core JavaScript files
* Usage analytics are captured correctly
* Session replay functions without interruption
* The AI agent can communicate with backend services
## Required Domains
[Section titled “Required Domains”](#required-domains)
Add the following domains to your firewall allow-list:
| Domain | Purpose |
| :------------------------------- | :---------------------------------------------------------------------------- |
| `https://script.eucerahive.io` | Hosts the JavaScript files required by the SDK to initialize and run. |
| `https://rte.eucerahive.io` | Tracks application usage events for analytics and performance insights. |
| `https://recorder.eucerahive.io` | Handles session recording for replay and debugging features. |
| `https://agent.eucerahive.io` | Connects to the AI agent backend for real-time assistance and task execution. |
# HMAC identity verification
> Enforce HMAC-SHA256 verification on identify calls so only authenticated, trusted users are tracked.
HMAC Identity Verification ensures every user tracked by the platform comes from an authenticated, trusted source. When enabled, the system enforces **HMAC validation** on all identify calls, blocking tracking of spoofed or unidentified users.
## When to use it
[Section titled “When to use it”](#when-to-use-it)
* To **guarantee trust** in user data across analytics and feature management.
* To **prevent spoofing**, where someone impersonates a user by sending fake events.
* To comply with **security and privacy policies** for verified user tracking.
## How it works
[Section titled “How it works”](#how-it-works)
1. An admin generates a **secret key** in the Identity Verification settings.
2. Developers use the secret to generate an **HMAC-SHA256 hash** of the user ID (usually `userId` or email), server-side.
3. The client SDK sends both the `userId` and the `userHash` with each identify call.
4. The platform validates the hash against the secret:
* If valid, the user is tracked.
* If invalid or missing, the user is rejected and not tracked.
```
sequenceDiagram
participant A as Admin
participant B as Your Backend
participant C as Client SDK
participant F as Foldspace
A->>B: Share secret key (from Settings)
Note over B: Secret stays server-side
B->>B: HMAC-SHA256(userId, secret)
B->>C: userId + userHash
C->>F: identify(userId, userHash)
F->>F: Recompute HMAC & compare
alt Hash matches
F-->>C: User tracked ✓
else Hash invalid or missing
F-->>C: User rejected ✗
end
```
## Enable Identity Verification
[Section titled “Enable Identity Verification”](#enable-identity-verification)
1. Go to **Settings → Identity Verification**.
2. Click **Generate** to create a new secret.
3. Copy the secret and store it securely (for example, in a key vault).
4. Share the secret with your development team to implement hashing.
5. Toggle **Enforce Identity Verification** to ON.
6. Save your changes.
Caution
Once enabled, **all identify calls must include a valid HMAC hash**. Unverified users will no longer be tracked.
## Parameters
[Section titled “Parameters”](#parameters)
| Field | Description |
| ---------- | ----------------------------------------------------------------------------------- |
| `userId` | Unique identifier of the user (e.g., UUID, email, database ID). |
| `userHash` | HMAC-SHA256 hash of `userId`, signed with the secret. Must be computed server-side. |
## Generate the user hash
[Section titled “Generate the user hash”](#generate-the-user-hash)
After enabling HMAC, update your identify calls to include `userHash`. The `userHash` is an HMAC-SHA256 hash of the `userId`, signed with the shared secret from the admin.
Caution
Generate the hash in **server-side code only**. Never expose the secret key in frontend code.
* Node.js
```js
const crypto = require("crypto");
function generateUserHash(secret, userId) {
return crypto.createHmac("sha256", secret).update(userId).digest("hex");
}
const userId = "user_123";
const secret = process.env.FOLDSPACE_SECRET;
const userHash = generateUserHash(secret, userId);
// Pass to the SDK
foldspaceClient.identify({
userId,
userHash
});
```
* Python
```python
import hmac
import hashlib
def generate_user_hash(secret, user_id):
return hmac.new(
secret.encode("utf-8"),
user_id.encode("utf-8"),
hashlib.sha256
).hexdigest()
user_id = "user_123"
secret = os.getenv("FOLDSPACE_SECRET")
user_hash = generate_user_hash(secret, user_id)
client.identify({
"userId": user_id,
"userHash": user_hash
})
```
## Related
[Section titled “Related”](#related)
* [JWT Authentication](/security/jwt/): protect agent sessions with signed tokens.
* [Authentication](/start/authentication/): API keys and scopes.
# JWT authentication
> Protect AI agent sessions with backend-signed, HS256 JWTs so users can only access their own conversations and actions.
JWT authentication ties every agent request to a backend-verified user identity. Use it on any production deployment so a user can only access their own conversations, history, and actions, and so requests can’t be forged from the client.
## Why use JWT
[Section titled “Why use JWT”](#why-use-jwt)
Without authentication, a malicious actor could impersonate a user, read private conversations, or invoke actions on their behalf. JWT authentication guarantees:
* A user can only access *their own* conversations and data.
* Requests to the agent can’t be forged from the client side.
* Conversation history and agent actions are protected from identity theft.
## What is a JWT?
[Section titled “What is a JWT?”](#what-is-a-jwt)
A **JSON Web Token (JWT)** is a compact, signed token used to transmit identity information between systems. A JWT:
* Is generated by a trusted backend.
* Is cryptographically signed with a secret key.
* Can be verified by a server without additional database lookups.
* Has a built-in expiration time.
In Foldspace, the JWT acts as **proof of identity** for the user interacting with the agent. Requiring a server-signed token means user identity can’t be spoofed, only authenticated users can send messages or retrieve conversations, and tokens expire automatically to limit exposure if compromised.
## How it works
[Section titled “How it works”](#how-it-works)
1. You generate a **secret key** in the Foldspace dashboard.
2. Your backend uses the secret to sign a JWT with **HS256**.
3. The signed JWT is sent to the browser.
4. The Foldspace Web SDK includes the JWT in every request.
5. Foldspace verifies the token and user identity on every call.
```
sequenceDiagram
participant B as Your Backend
participant C as Browser
participant S as Foldspace SDK
participant F as Foldspace
B->>B: Sign JWT (HS256) with secret key
B->>C: Return signed JWT
C->>S: Initialize with token
S->>F: Request + JWT header
F->>F: Verify signature & expiry
F-->>S: Authenticated response
Note over S,F: On expiry, SDK calls onTokenExpired
S->>C: Request fresh token
C->>B: Fetch new JWT
B->>C: New signed JWT
C->>S: Return token
```
## Step 1: Generate a secret key
[Section titled “Step 1: Generate a secret key”](#step-1-generate-a-secret-key)
JWT signing keys are managed under **Settings → Identity Verification**. You can generate one or more **JWT secret keys**.
* These keys sign JWTs in your backend.
* They must be kept **private and secure**.
* They must **never** be exposed in frontend code.
Once generated, copy the secret key and store it securely, for example, as an environment variable.
### Access control
[Section titled “Access control”](#access-control)
To **copy a secret key** or **change a key’s status**, a Foldspace user must have the **Jwt Settings Admin** role. Users without this role can view configuration but can’t access or modify secret keys.
## Rotate keys
[Section titled “Rotate keys”](#rotate-keys)
Foldspace supports key rotation so you can maintain security without service disruption. You manage rotation, and multiple secret keys can coexist for safe transitions in production.
Each secret key has a status that controls how it’s enforced.
### Secret key statuses
[Section titled “Secret key statuses”](#secret-key-statuses)
| Status | Enforcement | Notes |
| ------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INACTIVE` | Not enforced | Default status when a key is created. The key can be copied and safely prepared for deployment. Can be moved to `INACTIVE` from any status except `REVOKED`. |
| `ACTIVE` | Fully enforced | Tokens signed with this key are accepted by Foldspace. |
| `TESTING` | Evaluated, not enforced | Validates JWT signing without enforcing authentication. Only one `TESTING` key is allowed at a time. Verification results are returned in a response header. Does not affect production traffic or agent availability. |
| `DEPRECATED` | Valid and enforced | Used during key rotation. Lets existing backend deployments keep working while a new key is rolled out. |
| `REVOKED` | Rejected | Permanently disabled and deleted. Tokens signed with this key are rejected. Status can’t be changed once revoked. |
### Recommended rotation flow
[Section titled “Recommended rotation flow”](#recommended-rotation-flow)
1. Create a new secret key (initially `INACTIVE`).
2. Deploy the new key to your backend.
3. Move the new key to `ACTIVE`.
4. Move the old key to `DEPRECATED`.
5. Once fully migrated, move the deprecated key to `REVOKED`.
This rotates keys with no downtime.
```
stateDiagram-v2
[*] --> INACTIVE: Key created
INACTIVE --> ACTIVE: Deploy & activate
INACTIVE --> TESTING: Validate first
TESTING --> ACTIVE: Promote
ACTIVE --> DEPRECATED: New key activated
DEPRECATED --> REVOKED: Migration complete
REVOKED --> [*]
note right of TESTING: One TESTING key at a time. Not enforced — result in header.
note right of DEPRECATED: Still accepted. Gives backends time to migrate.
```
## Step 2: Sign the JWT in your backend
[Section titled “Step 2: Sign the JWT in your backend”](#step-2-sign-the-jwt-in-your-backend)
Create the JWT **only in your backend** and sign it with the **`HS256`** algorithm. Tokens signed with any other algorithm are rejected.
### Required payload
[Section titled “Required payload”](#required-payload)
```json
{
"userId": "",
"exp":
}
```
### Signing requirements
[Section titled “Signing requirements”](#signing-requirements)
| Requirement | Value |
| ----------- | --------------------------------------------- |
| Algorithm | `HS256` |
| Secret | `ACTIVE` or `DEPRECATED` Foldspace secret key |
| Location | Backend only |
### Code examples
[Section titled “Code examples”](#code-examples)
* Node.js
```js
import jwt from "jsonwebtoken";
const FOLDSPACE_SECRET = process.env.FOLDSPACE_JWT_SECRET;
function generateFoldspaceToken(userId) {
const expiresInSeconds =
Math.floor(Date.now() / 1000) + (15 * 60);
return jwt.sign(
{ userId, exp: expiresInSeconds },
FOLDSPACE_SECRET,
{ algorithm: "HS256" }
);
}
```
* Python
```python
import jwt
import time
import os
FOLDSPACE_SECRET = os.environ["FOLDSPACE_JWT_SECRET"]
def generate_foldspace_token(user_id):
payload = {
"userId": user_id,
"exp": int(time.time()) + 900
}
return jwt.encode(
payload,
FOLDSPACE_SECRET,
algorithm="HS256"
)
```
* Ruby
```ruby
require 'jwt'
secret = ENV['FOLDSPACE_JWT_SECRET']
def generate_foldspace_token(user_id, secret)
payload = {
userId: user_id,
exp: Time.now.to_i + 900
}
JWT.encode(payload, secret, 'HS256')
end
```
## Step 3: Pass the JWT to the Web SDK
[Section titled “Step 3: Pass the JWT to the Web SDK”](#step-3-pass-the-jwt-to-the-web-sdk)
Provide the signed JWT to the Foldspace Web SDK through the **`agent`** API:
```js
// 1. Fetch the token from your backend API
const response = await fetch('/api/auth/foldspace-token');
const { token } = await response.json();
// 2. Initialize Foldspace agent via the agent API
const agent = foldspace.agent({
apiName: 'the-agent-api-name',
token: token,
onTokenExpired: async () => {
const response = await fetch('/api/auth/foldspace-token');
const { token } = await response.json();
console.log('token refreshed', token);
return token;
}
});
agent.show();
```
Here, the frontend fetches a signed JWT from your backend and passes it via the `token` property. The SDK attaches the token to every request. When the token expires, the SDK calls `onTokenExpired`, and your UI fetches and returns a new token.
## Test your signed JWT
[Section titled “Test your signed JWT”](#test-your-signed-jwt)
Use a `TESTING` key to validate JWT signing before you enforce authentication in production.
1. Move a secret key to `TESTING` (only one key can be in `TESTING` at a time).
2. Sign a JWT with the `TESTING` key.
3. Send requests using that JWT via the Foldspace SDK.
4. Foldspace evaluates the token and returns the result in a response header.
### Response header
[Section titled “Response header”](#response-header)
For requests signed with a `TESTING` key, Foldspace returns the verification result in this header:
Header
```text
X-Jwt-Testing-Result
```
| Value | Meaning |
| ----------- | --------------------------------------------------- |
| `validated` | The JWT is correctly signed and structured. |
| `failed` | The JWT is invalid, expired, or incorrectly signed. |
This lets you verify signature correctness, payload structure, and expiration handling without impacting production users.
Note
JWTs signed with `ACTIVE` or `DEPRECATED` keys are enforced and must be valid. JWTs signed with a `TESTING` key are evaluated but not enforced: the request proceeds and the result is surfaced only via the response header. This enables production-safe validation before key rotation.
Use a `TESTING` key when you want to validate a new backend JWT implementation, confirm the signing algorithm (`HS256`), verify payload structure and expiration handling, or test new keys before promoting them to `ACTIVE`.
## Choose a secure expiration time
[Section titled “Choose a secure expiration time”](#choose-a-secure-expiration-time)
* Use short-lived tokens (5–30 minutes).
* Refresh tokens from your backend when needed.
Short expiration times reduce risk and align with modern security standards.
## Error handling and token lifecycle
[Section titled “Error handling and token lifecycle”](#error-handling-and-token-lifecycle)
**Token expired.** The SDK invokes `onTokenExpired`. Fetch a new JWT with a fresh `exp` from your backend and return it to the SDK.
**Invalid token.** If the token is invalid or can’t be verified, all agent requests are rejected, the agent is automatically hidden from the UI, and user data and actions remain protected.
## Related
[Section titled “Related”](#related)
* [HMAC Identity Verification](/security/hmac/): verify identify calls server-side with HMAC-SHA256.
* [Authentication](/start/authentication/): API keys and scopes.
# Security
> Verify user identity and restrict where the agent can run.
Two kinds of control keep the agent secure: **verify who the user is** so their identity can’t be spoofed, and **restrict where the agent runs** so your Agent Key can’t be used on other sites.
## Verify identity
[Section titled “Verify identity”](#verify-identity)
Sign the user context you pass to `identify()` so the agent trusts it. Pick one:
[JWT ](/security/jwt/)Verify user identity with a signed JSON Web Token.
[HMAC ](/security/hmac/)Verify user identity with an HMAC signature.
## Restrict where it runs
[Section titled “Restrict where it runs”](#restrict-where-it-runs)
[Domain allow list ](/security/domain-allow-list/)Limit the domains your agent is allowed to load on.
## Where to start
[Section titled “Where to start”](#where-to-start)
If you pass user context, add [JWT](/security/jwt/) or [HMAC](/security/hmac/) signing so it can’t be forged. Then lock down the [Domain allow list](/security/domain-allow-list/).
## Compliance
[Section titled “Compliance”](#compliance)
Foldspace meets [enterprise-grade compliance standards](/privacy/compliance/) — ISO 27001, SOC 2, and GDPR.
# Install in Claude Code
> Install the Foldspace plugin in Claude Code, sign in, verify the MCP server, and let the agent wire Foldspace into your app.
Install the Foldspace plugin once and Claude Code does the integration for you: it adds the agent, connects your authenticated users, and discovers your product’s actions. The whole flow is a marketplace add, one install, a browser sign-in, and a prompt.
## Pick your plugin
[Section titled “Pick your plugin”](#pick-your-plugin)
Foldspace ships two plugins. The only question that decides which you need is whether Claude can edit the site’s source code.
* Codebase (you own the code)
Use **`foldspace-codebase-plugin`** when Claude Code can edit the site’s frontend. It wires the Foldspace SDK directly into your app: adds the loader, identifies your users, and scaffolds your actions in code. This is the common case.
* Remote (you can't edit the code)
Use **`foldspace-remote-plugin`** when you can’t change the source, for example when prototyping against someone else’s live site. It builds a Chrome extension that injects Foldspace into the running page, and also starts Chrome DevTools MCP so the agent can inspect the live site.
Tip
Not sure? Pick **Codebase**. If Claude reports it can’t find or edit the frontend, switch to **Remote**.
## Install
[Section titled “Install”](#install)
1. **Add the Foldspace marketplace** (one time per machine):
```sh
claude plugin marketplace add foldspace-ai/plugins
```
2. **Install the plugin that matches your project** from the [Pick your plugin](#pick-your-plugin) choice above:
* Codebase (you own the code)
```sh
claude plugin install foldspace-codebase-plugin@foldspace-plugins
```
* Remote (you can't edit the code)
```sh
claude plugin install foldspace-remote-plugin@foldspace-plugins
```
3. **Sign in.** There’s no API key to paste. Run `/mcp`, pick **foldspace**, and Foldspace opens a browser window for you to sign in. Claude Code stores the resulting OAuth token itself and reuses it on later sessions.
4. **Verify everything loaded.** Inside Claude Code, run:
```text
/plugin # confirms the Foldspace plugin is installed
/mcp # confirms the Foldspace MCP server is connected
/agents # confirms the plugin's agents are available
```
5. **Run the integration.** Ask the agent:
> Integrate Foldspace into this app.
It adds the agent, identifies your users, and discovers your actions for you.
## Add the hosted MCP server instead
[Section titled “Add the hosted MCP server instead”](#add-the-hosted-mcp-server-instead)
The plugin is the full integration path. If you only want Claude to reach your Foldspace account’s actions (no plugin, no local server), point it at the hosted MCP endpoint:
```sh
claude mcp add --transport http foldspace https://api.foldspace.ai/mcp
```
Using Claude on the web or desktop, add the same server as a custom connector. This link pre-fills the name and URL:
[**Add the Foldspace connector to Claude →**](https://claude.ai/customize/connectors?modal=add-custom-connector\&connectorName=Foldspace\&connectorUrl=https%3A%2F%2Fapi.foldspace.ai%2Fmcp)
## Next steps
[Section titled “Next steps”](#next-steps)
Keep going
* New to the SDK? See [Setup](/start/install/) for the manual wiring the plugin automates.
* Using a different editor (Cursor, Windsurf, VS Code)? See [Vibe coding](/guides/vibe-coding/).
* Browse the plugins on [GitHub](https://github.com/foldspace-ai/plugins).
# FAQ
> Common questions about how Foldspace works, integrates, secures data, and drives product adoption with an AI-Native interface and Conversational Analytics.
## Product Understanding
[Section titled “Product Understanding”](#product-understanding)
### What is Foldspace in one line?
[Section titled “What is Foldspace in one line?”](#what-is-foldspace-in-one-line)
Foldspace is an AI-Native conversational interface where users say what they want (“add as an admin,” “create a ticket,” “analyze my last campaign”) and the agent does it.
### What exactly does Foldspace do in one sentence?
[Section titled “What exactly does Foldspace do in one sentence?”](#what-exactly-does-foldspace-do-in-one-sentence)
Foldspace lets SaaS companies embed an AI-Native interface inside their product, so users skip menus and dashboards and go from intent to execution instantly, with Conversational Analytics on every interaction.
### How is Foldspace different from a chatbot or AI assistant?
[Section titled “How is Foldspace different from a chatbot or AI assistant?”](#how-is-foldspace-different-from-a-chatbot-or-ai-assistant)
A chatbot answers questions. An AI assistant handles small tasks. Foldspace’s Product Agent navigates, acts, and executes multi-step workflows across your product, and every interaction feeds Conversational Analytics. It’s fused into your product’s core experience, not bolted on.
### What kinds of workflows can Foldspace automate?
[Section titled “What kinds of workflows can Foldspace automate?”](#what-kinds-of-workflows-can-foldspace-automate)
Anything slowed by menus, dashboards, or training:
* Multi-step onboarding flows
* Reporting and analytics queries
* Data entry and record management
* Cross-app workflows (CRM → billing → support)
* Contextual “next best action” suggestions
### Does it work better for complex enterprise products or also lighter SaaS apps?
[Section titled “Does it work better for complex enterprise products or also lighter SaaS apps?”](#does-it-work-better-for-complex-enterprise-products-or-also-lighter-saas-apps)
Foldspace excels with complex SaaS that have steep learning curves (HR, healthcare, finance, analytics). Lighter SaaS apps also benefit, gaining an AI-Native edge without rebuilding their UI.
### How do the Product Agent, Task Agents, and Chatterblocks differ?
[Section titled “How do the Product Agent, Task Agents, and Chatterblocks differ?”](#how-do-the-product-agent-task-agents-and-chatterblocks-differ)
* **Product Agent** → runs workflows end-to-end from plain-language intents (e.g., “Set up brand settings”).
* **Task Agents (AI Functions)** → prompt-less features (often a “Generate” button) that return structured outputs such as drafts, summaries, or auto-fill.
* **Chatterblocks** → in-thread UI components (forms, cards, tables, or charts) to review data, preview changes, and confirm actions inside the conversation.
### What is “Product Knowledge” in practice?
[Section titled “What is “Product Knowledge” in practice?”](#what-is-product-knowledge-in-practice)
Product Knowledge combines docs, help articles, and contextual data with user actions. It ensures accurate answers, activates features with best practices, and guides users to successful outcomes.
## Value & ROI
[Section titled “Value & ROI”](#value--roi)
### How does Foldspace impact product adoption metrics?
[Section titled “How does Foldspace impact product adoption metrics?”](#how-does-foldspace-impact-product-adoption-metrics)
Foldspace simplifies the path to value. Users express outcomes directly, leading to faster activation, stronger feature usage, and higher adoption.
### What kind of ROI do customers see in the first 90 days?
[Section titled “What kind of ROI do customers see in the first 90 days?”](#what-kind-of-roi-do-customers-see-in-the-first-90-days)
* Faster new-user productivity
* Fewer “how do I” support tickets
* More consistent use of core product features
The ROI comes from reducing friction and boosting engagement.
### Does this reduce onboarding or training costs for new users?
[Section titled “Does this reduce onboarding or training costs for new users?”](#does-this-reduce-onboarding-or-training-costs-for-new-users)
Yes. Foldspace acts as an embedded guide, replacing training sessions or manuals. Users learn by asking and doing, shortening onboarding cycles.
### How fast can we get to value?
[Section titled “How fast can we get to value?”](#how-fast-can-we-get-to-value)
Text-based actions can launch within minutes; Chatterblock-enabled experiences in 2–3 hours. Teams typically ship meaningful outcomes the same day.
### How quickly can my team implement and ship Foldspace?
[Section titled “How quickly can my team implement and ship Foldspace?”](#how-quickly-can-my-team-implement-and-ship-foldspace)
Most teams go from proof-of-concept to live agent within a single sprint. Individual experiences built with AI coding tools such as Claude Code or Cursor take minutes to hours; see [Vibe coding](/guides/vibe-coding/).
## Technical Fit & Implementation
[Section titled “Technical Fit & Implementation”](#technical-fit--implementation)
### Do we need to rewrite our UI or create a dedicated backend?
[Section titled “Do we need to rewrite our UI or create a dedicated backend?”](#do-we-need-to-rewrite-our-ui-or-create-a-dedicated-backend)
No. Foldspace reuses your existing UI components and acts against your existing APIs/workflows. No parallel service layer required.
### Does Foldspace work with my existing frontend framework?
[Section titled “Does Foldspace work with my existing frontend framework?”](#does-foldspace-work-with-my-existing-frontend-framework)
Yes. Foldspace works with your existing frontend frameworks, including React, Angular, and Vue, and requires no changes to your existing backend endpoints or APIs. The SDK drops into the pages you already ship, and actions map to the APIs you already expose.
### How does Foldspace integrate with my current SaaS stack?
[Section titled “How does Foldspace integrate with my current SaaS stack?”](#how-does-foldspace-integrate-with-my-current-saas-stack)
On top of what you already run, with no changes to it. The SDK drops into your existing frontend (any framework, SPA or MPA), and actions execute against your existing backend APIs; nothing is rewritten, wrapped, or duplicated. See [Setup](/start/install/).
### What kind of developer effort is required to embed it?
[Section titled “What kind of developer effort is required to embed it?”](#what-kind-of-developer-effort-is-required-to-embed-it)
Minimal, and nothing in your existing frontend or backend changes. Add the SDK script to your app, define actions in Agent Studio, and register handlers that call the APIs you already have. Foldspace handles intent matching, orchestration, context, and the conversational UI. See [Connect actions](/guides/connecting-actions/).
### What languages, frameworks, or APIs are supported?
[Section titled “What languages, frameworks, or APIs are supported?”](#what-languages-frameworks-or-apis-are-supported)
Foldspace is framework-agnostic. It works with existing frontend frameworks such as React, Angular, and Vue, and with any backend that has API access. No changes to either are required; action handlers simply call the APIs your product already uses.
### Can it handle multi-tenant enterprise environments?
[Section titled “Can it handle multi-tenant enterprise environments?”](#can-it-handle-multi-tenant-enterprise-environments)
Yes. Foldspace enforces tenant boundaries and role-based access. Each session is scoped to tenant, user, and page context.
### How does Foldspace interact with our APIs and workflows?
[Section titled “How does Foldspace interact with our APIs and workflows?”](#how-does-foldspace-interact-with-our-apis-and-workflows)
Through Actions mapped to APIs/workflows. The agent can create, update, and retrieve data. With Chatterblocks, users can review and confirm changes inline.
### Do you support single-page applications (SPAs)?
[Section titled “Do you support single-page applications (SPAs)?”](#do-you-support-single-page-applications-spas)
Yes. Foldspace natively supports both single-page applications (SPAs) and multi-tab applications (MTAs).
## Reliability & Quality
[Section titled “Reliability & Quality”](#reliability--quality)
### How does it protect against AI hallucinations or inaccurate outputs?
[Section titled “How does it protect against AI hallucinations or inaccurate outputs?”](#how-does-it-protect-against-ai-hallucinations-or-inaccurate-outputs)
Foldspace executes against actual product logic and APIs. Guardrails, context validation, and configurable fallbacks ensure safe, accurate outcomes.
## Security & Compliance
[Section titled “Security & Compliance”](#security--compliance)
### How does Foldspace handle authentication and permissions?
[Section titled “How does Foldspace handle authentication and permissions?”](#how-does-foldspace-handle-authentication-and-permissions)
It respects your app’s existing model. The agent acts as the authenticated user, never bypassing access controls.
### How do you verify user identity?
[Section titled “How do you verify user identity?”](#how-do-you-verify-user-identity)
Foldspace supports two complementary mechanisms: JWT authentication ties every agent request to a backend-verified user identity, so users can only access their own conversations and actions, and HMAC identity verification signs identify calls so only authenticated, trusted users are tracked. See [JWT](/security/jwt/) and [HMAC](/security/hmac/).
### What about sensitive data?
[Section titled “What about sensitive data?”](#what-about-sensitive-data)
Foldspace supports PII masking/redaction. Sensitive data is excluded from prompts, logs, and models. Foldspace is SOC 2 Type 2 certified, ISO 27001 certified, and GDPR compliant.
### Where is data processed and stored?
[Section titled “Where is data processed and stored?”](#where-is-data-processed-and-stored)
Encrypted in transit and at rest, on GCP with both US and EU datacenters. The EU region uses locally available AI models from Google and OpenAI. Sensitive data is never used for training.
### What security certifications or enterprise compliance standards does Foldspace meet?
[Section titled “What security certifications or enterprise compliance standards does Foldspace meet?”](#what-security-certifications-or-enterprise-compliance-standards-does-foldspace-meet)
Foldspace is SOC 2 Type 2 certified, ISO 27001 certified, and GDPR compliant. See [Compliance](/privacy/compliance/) for details.
## Models & Flexibility
[Section titled “Models & Flexibility”](#models--flexibility)
### What models do you support?
[Section titled “What models do you support?”](#what-models-do-you-support)
Foldspace supports OpenAI, Google Gemini, and private models. It auto-selects models to balance cost, quality, and latency.
## Observability & Analytics
[Section titled “Observability & Analytics”](#observability--analytics)
### How do we observe and debug agent behavior?
[Section titled “How do we observe and debug agent behavior?”](#how-do-we-observe-and-debug-agent-behavior)
Use Conversational Analytics for full logs of queries, responses, and actions, and Action Insights to track execution and adoption. Sentiment, unanswered cases, and workflow trends surface in the dashboard, so you can debug and continuously improve experiences.
## Pricing & Plans
[Section titled “Pricing & Plans”](#pricing--plans)
### How is Foldspace priced?
[Section titled “How is Foldspace priced?”](#how-is-foldspace-priced)
Foldspace is subscription-based, priced on monthly active users (MAUs), not tokens. Every plan includes the AI-Native interface, Conversational Analytics, and product usage analytics. Plans run from Starter through Kinetic and Scale to Enterprise; see [foldspace.ai/pricing](https://foldspace.ai/pricing) for details.
### Will token usage drive up our cost?
[Section titled “Will token usage drive up our cost?”](#will-token-usage-drive-up-our-cost)
No. Pricing is per MAU. Foldspace continuously optimizes to reduce token cost while increasing accuracy and speed.
### Is there a startup program?
[Section titled “Is there a startup program?”](#is-there-a-startup-program)
Yes. Foldspace offers a startup program; contact sales to apply.
# Setup
> Install the Foldspace SDK, identify the user so conversation history persists, and show the agent.
Install the Foldspace SDK with one snippet. Copy yours from **Agent Studio → Setup** (pre-filled with your loader URL and Agent Key) and paste it before the closing `