# Agent Observability Source: https://docs.upsolve.ai/ai-agent-builder/agent-observability Inspect the step-by-step trace of how your agent answered a question, and the evaluations that graded it. Every answer your agent produces is the result of a sequence of steps: retrieving schema, looking up golden examples, generating SQL, executing it, building a chart, and writing the narrative. Agent Observability gives you the full trace of that process — plus the automated evaluations that graded the result — so you can see exactly why an answer came out the way it did. ## Reaching a trace **From a live chat** — after the agent responds, click **Observability →** beneath the answer. The trace opens in the right-hand panel and follows the conversation as it continues. A chat answer with the Observability link in its action row, and the trace panel opening on the right-hand side **From Chat History** — open [Chat History](/ai-agent-builder/chat-history), click a row, and switch the sidebar to the **Observability** tab. This works for every conversation, including deployed end-user chats and MCP calls. The tab is hidden for conversations that made no tool calls, because there is nothing to trace. The Chat History detail sidebar with the Observability tab selected, showing the trace instead of the conversation ## What the trace shows ### The summary bar At the top of the trace, a row of chips summarises the whole conversation: * **steps** — total number of tool calls the agent made. * **duration** — total time spent inside those calls, in milliseconds. * **rag** — how many golden queries and golden charts were retrieved as references. * **evals** — how many automated evaluations ran. The chip is green when all of them passed and amber when any did not. * **errors** — present only when at least one step failed. **Expand** and **Collapse** open or close every step at once. The trace view, with a summary chip bar of steps, duration, RAG sources and evals above the agent pipeline steps, the agent response and the evaluations ### Per-exchange sections Below the summary, the trace is broken into one section per user question, each with up to four blocks: **User Query** — the question as the user asked it. **Agent Pipeline** — the ordered list of steps the agent took. Every step shows its own duration and success/error state, and expands to reveal its inputs and outputs. The steps you'll see include: | Step | What it did | | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Schema Retrieval** | Searched the data model for the tables and columns relevant to the question. | | **Selectable Values** | Searched actual column values, so a filter on a category name resolves to a value that exists. | | **Full Schema** | Loaded the whole data model — used for "what data do I have?" style questions. | | **Golden Examples** | Retrieved confirmed question → SQL → chart pairs to use as references. | | **SQL Generation** | Wrote the query. Shows the task it was given, the schema it was handed, the golden references it used, and the SQL it produced. If a number is wrong, start here. | | **SQL Validation** | Checked the query against the database before running it for real. | | **Data Retrieval** / **SQL Execution** | Ran the query and returned rows. Shows the row count and the columns that came back. | | **Chart Generation** / **Render Chart** / **Chart Validation** | Chose the chart type, axis mappings and formatting. If the visualization is wrong but the numbers are right, the config here tells you what the agent picked. | | **Knowledge Base Search** / **Knowledge Base File** | Searched or read a [Knowledge Base](/ai-agent-builder/knowledge-base) document. | | **Skill** / **Skill Search** / **Skill Read** | Loaded a [Skill](/ai-agent-builder/skills) on demand. Internal plumbing skills are hidden; your own and learned skills are shown. | | **Report Generation** / **Shareable Report** | Produced a CSV/PDF export or a shareable analysis. | | **Suggestions** | Generated the follow-up questions offered under the answer. | | **Learning Signal** | Recorded a correction the agent detected — the stage that went wrong, the failing artifact, and the user's correction. | Tools you connected through [MCP](/ai-agent-builder/mcp-context) appear here too, under their own names. **RAG Sources** — wherever a step used golden assets, the trace lists them as cards showing the retrieved question, its SQL, and a preview of its chart, each with the similarity score that earned it a place. When a trace shows the agent leaning on a bad example, the card names the [Golden Asset](/ai-agent-builder/golden-assets) to go and fix. The RAG Sources block in a trace, with the golden examples step expanded to list the SQL and chart references it retrieved, each card showing its similarity score **Agent Response** — the narrative the user actually read. **Evaluations** — the automated grades for this exchange. See below. ## Evaluations Upsolve runs continuous, LLM-based evaluations against production traces. They run automatically in the background — you don't trigger them, and they don't slow the agent's answer down — and their verdicts flow back into the **Evals** column in [Chat History](/ai-agent-builder/chat-history) and into the **Evaluations** block of the trace. ### Reading a verdict Each evaluation resolves to one of three states: * **Pass** — the evaluation's criterion was met. * **Fail** — the criterion was not met, or the evaluation itself errored. * **Pending** — the evaluation hasn't returned a verdict yet. Evaluations run asynchronously, so a very recent conversation will often show pending for a short while. The row-level verdict in Chat History is the worst state present: any failure makes the row fail, otherwise any pass makes it pass, otherwise it's pending. Hover the pill for the per-evaluation breakdown. Expand an evaluation in the trace to see: * **Score** — the numeric grade the evaluator returned, when it produced one. * **Description** — what this evaluation checks. * **Annotation** — the evaluator's written reasoning for the verdict it gave. This is the part worth reading: it usually names the specific thing that went wrong. The Evaluations block of a trace, listing each evaluation with its verdict, and one expanded to show its score, description and the evaluator's written annotation ### What gets evaluated Evaluations fall into two broad families. The exact set is configured per deployment, so talk to your Upsolve contact to add one or tune an existing one for your agent. **Answer quality** — did the agent actually answer the question, and is the answer right? *Answer Correctness Judge* is the workhorse here: it grades the response against the question that was asked and the data the agent retrieved to answer it. **Conversation health** — how the exchange is going across turns, rather than within a single answer. *Multi-Turn Forgetfulness Detector* catches the agent losing context established earlier in the conversation — a filter the user set three turns ago that silently stopped being applied. *User Frustration Detector* catches the user re-asking, correcting, or pushing back, which is the earliest reliable signal that something is wrong even when no step errored and no thumbs down was given. Conversation-health evaluations read the whole thread, so they only produce a meaningful verdict once a conversation has enough turns. A single-question session will typically show them as not applicable rather than pass or fail. Some evaluations run purely for Upsolve's own quality monitoring and are deliberately not surfaced in the trace, so the count in the **evals** chip can be lower than the number of evaluations that actually ran. ## Using traces to improve your agent Observability earns its keep when an answer is wrong. The common patterns: **Wrong SQL, right-sounding answer** — the agent generated plausible SQL that doesn't implement your business logic. Open **SQL Generation** and read the query. The fix is usually a more precise [System Prompt](/ai-agent-builder/system-prompts) rule, or a [Golden Query](/ai-agent-builder/golden-assets) demonstrating the correct approach. **No RAG sources retrieved for a common question** — the agent answered from scratch instead of referencing a validated example. Check whether a Golden Query covers this pattern, and whether the phrasing is semantically close enough to trigger retrieval. **The wrong golden asset was retrieved** — the trace shows a RAG source that doesn't fit the question. Note which asset the card names, then sharpen its question text in [Golden Assets](/ai-agent-builder/golden-assets), or retire it. **Correct SQL, wrong chart** — the data was right but the visualization misled. Add a [Golden Chart](/ai-agent-builder/golden-assets) for this question type. **Errors in the pipeline** — a red step names the failure directly. A failed SQL execution usually points at a data model that promises a column the warehouse doesn't have; a failed validation usually points at generated SQL the dialect rejects. **High cost or many steps** — the trace shows repeated SQL attempts before a correct result. That normally means the data model annotations or system prompt need more specificity, so the agent stops guessing. **A thumbs down with no obvious error** — read the **Annotation** on the failing evaluation, then the **Agent Response** block. Framing problems (over-claiming, missing caveats, too much jargon) don't surface as errors anywhere else. ## Next steps Browse conversations as sessions or traces, and filter on evals, feedback, errors and cost. Add example queries and charts to fix patterns you identify in traces. # Agents Source: https://docs.upsolve.ai/ai-agent-builder/agents Build AI chat experiences that understand your data. ## What is an Agent? An Agent is an AI-powered chat interface that: * Converts natural language questions into SQL * Analyzes results and provides insights * Creates visualizations from query results * Learns from examples (Golden Assets) Agent chat interface ## Agent Components ### Data Model Link Every agent is linked to a specific data model version. This ensures: * The agent knows what tables and columns exist * Changes to the data model don't break the agent * You can test with different data model versions ### System Prompt A custom prompt that shapes how the agent responds: * Company context and terminology * Response style preferences * Special instructions ### Golden Assets Example queries and charts that teach the agent: * How to write SQL for common questions * What visualizations to create * Correct terminology and calculations ### Evaluations (Evals) Test sets to verify agent accuracy: * Known question-answer pairs * SQL validation * Result comparison ## Creating an Agent 1. Navigate to your project's **Agents** tab 2. Click **Create Agent** 3. Enter a name and description 4. Select the data model to use 5. Click **Create** Create agent dialog ## Configuring Your Agent ### Setting the System Prompt 1. Open your agent 2. Go to the **System Prompt** tab in the sidebar 3. Write instructions for the agent 4. Click **Save** Example system prompt: ``` You are a data analyst for an e-commerce company. Use clear, business-friendly language. When users ask about "revenue," they mean gross_sales minus refunds. Always include date ranges in your analysis. ``` System prompt editor ### Adding Golden Assets Golden Assets are example queries that improve accuracy: 1. Go to the **Assets** tab 2. Click **Add Asset** 3. Enter the question and corresponding SQL 4. Optionally add a chart configuration 5. Click **Save** Golden assets list **Tip:** The more golden assets you add for common questions, the more consistent your agent becomes. ### Creating Evaluations 1. Go to the **Tests** tab 2. Click **Add Test** 3. Enter a question 4. Enter the expected SQL or answer 5. Save and run the test Agent test interface ## Admin View vs User View ### Admin View As an admin, you see full details: * Every tool call the AI makes * SQL queries generated * Step-by-step reasoning * Token usage and timing Admin chat view with tool calls expanded ### User View End users see a clean interface: * Just the question and answer * Charts and insights * No technical details User chat view ## Versioning Like data models, agents are versioned: 1. Go to the **Versions** tab 2. See all versions with timestamps 3. Each version tracks: assets, prompt, data model link Agent versions list ## Setting Production Before users can access your agent, you must set a version as production. ### Requirements 1. The agent must be linked to a data model 2. That data model version must already be production 3. The agent's schema requirements must fit within the data model ### Steps 1. Open your agent 2. Go to the **Versions** tab 3. Click **Set as Production** on the version you want 4. The system validates everything 5. If successful, users can now access this version Set agent as production ### Validation Errors If validation fails, you'll see specific errors: * "Data model version is not production" - Set the data model production first * "Missing tables/columns" - The agent uses data not in the data model ## Changing Data Model Version To link your agent to a different data model version: 1. Go to the **Data Model** tab in the sidebar 2. Select the data model 3. Choose a version (production versions recommended) 4. Click **Save** This creates a new agent version with the new data model link. Change data model version ## Best Practices ### 1. Start with Golden Assets Add 10-20 golden assets covering common questions before going live. ### 2. Use Descriptive Data Models Column descriptions in your data model help the agent understand your schema. ### 3. Run Evals Regularly Create a test suite and run it whenever you update the agent. ### 4. Monitor in Admin View Periodically check admin view to see how the agent handles real questions. ## Next Steps * [Build an Application](/ai-agent-builder/applications) to combine agents with dashboards * [Set up the complete flow](/ai-agent-builder/setup-guide) from project to production # Applications & Spaces Source: https://docs.upsolve.ai/ai-agent-builder/applications Build dashboard templates and deploy them to customer spaces. ## What is an Application? An Application is a container for dashboard templates that you deploy to your customers. It includes: * **Chart Templates** - Reusable chart configurations * **Dashboard Templates** - Layouts combining multiple charts * **Spaces** - Customer-specific instances of your templates * **Default Settings** - Language, timezone, export options Application with templates and spaces ## How Applications and Spaces Work ```mermaid theme={null} flowchart TD A[Application] --> B[Chart Templates] A --> C[Dashboard Templates] A --> D[Spaces] D --> E[Space for Org 1] D --> F[Space for Org 2] D --> G[Space for Org 3] ``` Each space receives its own private copies of your published templates. When you publish a template: 1. It becomes available to all spaces 2. Each space gets its own private copy 3. Users can customize their copies without affecting others ## Creating an Application 1. Navigate to your project's **Applications** tab 2. Click **Create Application** 3. Enter a name 4. Select a data model 5. Click **Create** Create application dialog ## Building Templates ### Creating a Chart Template 1. Open your application 2. Click **Add Chart** 3. Choose a chart type 4. Configure the data and visualization 5. Click **Save** Chart builder interface ### Creating a Dashboard Template 1. Open your application 2. Click **Add Dashboard** 3. Drag and drop charts onto the canvas 4. Arrange the layout 5. Add filters if needed 6. Click **Save** Dashboard builder interface ## Viewing as a User While building templates, you see them as an admin. To see what users will see: 1. Select a project user from the dropdown 2. The view switches to their perspective 3. Data is filtered by their RLS rules 4. Check that the template works for different users User impersonation dropdown ## Publishing Templates Templates start in "draft" mode. To make them available to users: 1. Open the template 2. Click **Publish** 3. The template propagates to all spaces Publish button on template ### What Happens When You Publish 1. Template status changes to "published" 2. Each space gets a copy of the dashboard 3. Users can now see and interact with it 4. Future updates require re-publishing ## Understanding Spaces Each project organization automatically gets a Space for each application. ### What's in a Space? * **Template Copies** - Private instances of published templates * **User Dashboards** - Dashboards users create themselves * **User Charts** - Charts users create themselves ### Viewing Spaces 1. Open your application 2. Go to the **Spaces** tab in the sidebar 3. See all spaces (one per organization) 4. Click to preview any space List of spaces in application ## Default Views You can set which dashboard users see first when they open their space. ### Setting an Application Default 1. Open your application 2. Go to the **Templates** tab 3. Click the star icon next to a template 4. This becomes the default for all users Setting default template ### User-Specific Defaults Users can change their own default: 1. Open any dashboard in their space 2. Click **Set as Default** 3. This overrides the application default for them only ## Application Settings ### Default Language Set the default language for your application: 1. Go to application settings 2. Select default language 3. Users can override in their preferences ### Default Timezone Configure how dates are displayed: 1. Go to application settings 2. Set the default timezone offset 3. Enable "Use Local Timezone" for auto-detection ### Export Settings Control whether users can export data: 1. Go to application settings 2. Toggle "Allow Exports" Application settings panel ## Production Status Like data models and agents, applications can be set to production. ### Requirements 1. Application must have a linked data model 2. That data model version must be production 3. All template schemas must fit within the data model ### Setting Production 1. Open your application 2. Go to the **Versions** tab 3. Click **Set as Production** 4. Validation runs automatically ## Connecting Agents and Applications If an agent and application share the same data model version: * The agent can generate charts compatible with the application * Users can save agent-generated charts to their space * Charts flow seamlessly between AI and dashboards ## Best Practices ### 1. Test with Multiple Users Always preview templates as different users to ensure RLS works correctly. ### 2. Start with Core Dashboards Create 2-3 essential dashboards before adding more. ### 3. Use Consistent Naming Name templates clearly so users understand what they show. ### 4. Document in Descriptions Add descriptions to dashboards explaining what they display. ## Next Steps * Follow the [Complete Setup Guide](/ai-agent-builder/setup-guide) for the full workflow * Learn about [Row-Level Security](/ai-agent-builder/rls) for data filtering # Chat History Source: https://docs.upsolve.ai/ai-agent-builder/chat-history Review every conversation your agent has handled, as sessions or as individual traces. Chat History is the operational view of your agent. It lists every conversation the agent has handled — sessions run in Agent Studio, live chats from deployed end users, and calls that came in over MCP — with the quality, cost and reliability signals for each one attached to the row. It answers two different questions, and it has a view for each: * **Sessions** — "how is this conversation going?" One row per conversation. * **Traces** — "how did this answer turn out?" One row per user question → agent answer exchange. Use the **Sessions / Traces** toggle in the toolbar to switch. Both views share the same filters, column controls, time range and CSV export; only the grain changes. ## Sessions view One row per conversation. Available columns: | Column | What it shows | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | **Date** | When the conversation started. | | **Evals** | Aggregate verdict of the automated evaluations that ran on this conversation — pass, fail, or pending. Hover for the per-eval breakdown. | | **Summary** | A short generated description of what the conversation was about. | | **Feedback** | The thumbs up / thumbs down the user submitted, if any. Hover to read the written comment. | | **Charts** | How many charts the agent built. | | **Errors** | How many steps failed — a failed SQL execution, a tool error, a rejected query. | | **Questions** | How many questions the user asked in the conversation. | | **RAG Sources** | How many golden queries and golden charts were retrieved as references. | | **Cost** | Model spend for the conversation, in USD. | | **Duration** | Wall-clock length of the session. | | **User** | The project user who was chatting, or *Admin* for a builder session in Agent Studio. | | **Platform** | Where the conversation came from — *Agent Studio*, *Embedded*, or *MCP*. | **Duration**, **User** and **Platform** are hidden by default. Turn them on from the column picker. The Chat History table in Sessions mode, one row per conversation, with columns for evals, summary, feedback, charts, errors, questions, RAG sources and cost ## Traces view One row per exchange, so a five-question conversation produces five rows. This is the grain to work at when you're triaging quality: an eval failure, a thumbs down or an error belongs to a specific answer, not to the whole session. | Column | What it shows | | ---------------- | ------------------------------------------------ | | **Time** | When the exchange happened. | | **Evals** | The evaluation verdict for this specific answer. | | **User Input** | The question as the user asked it. | | **Agent Output** | The answer the agent gave. | | **Feedback** | Thumbs up / down on this answer. | | **Charts** | Charts produced by this answer. | | **Errors** | Failed steps within this answer. | | **RAG Sources** | Golden assets retrieved for this answer. | | **Cost** | Model spend attributed to this exchange. | | **User** | Who asked. | | **Platform** | Where the question came from. | | **Trace ID** | The observability trace identifier. | | **#** | The exchange's position within its conversation. | **User**, **Platform**, **Trace ID** and **#** are hidden by default. The Chat History table in Traces mode, one row per user question and agent answer, with the user input and agent output shown inline ## Working the table **Filters** — the chip bar on the left of the toolbar filters on any visible column: eval status, feedback, platform, user, and numeric ranges for charts, errors, questions, RAG sources and cost. Filters follow column visibility: hiding a column hides its filter but keeps the filter's value, so toggling the column back on restores it. **Time range** — narrow to the past 5 minutes, 30 minutes, hour, day, week, month, 3 months or year. Defaults to all time. **Columns** — the column picker lets you show and hide columns and drag them into the order you want. Columns can also be resized by dragging their edge in the header. Your choices persist per view; **Reset to defaults** in the picker restores the shipped layout. **Reload** — re-fetch without leaving the page. Useful when you're watching a live deployment. **Export CSV** — exports the current view with the columns you have visible, so the export matches what you're looking at. The Chat History toolbar with the column picker open, showing each column as a draggable checkbox row, alongside active filter chips and the time-range picker ## Opening a conversation Click any row to open the detail sidebar. The metadata card at the top carries **Date**, **Duration**, **Tenant**, **Agent**, **Platform**, **Cost**, the **Session ID**, and — when you opened the sidebar from a Traces row — the **Trace ID** for that exchange. Both identifiers can be copied with one click. If the user left feedback, **Feedback** and their **Comment** appear underneath. Below the metadata card are two tabs: * **Chat** — the conversation exactly as it happened, including the charts the agent rendered. * **Observability** — the step-by-step trace of what the agent did. This tab only appears for conversations that actually made tool calls. The Chat History detail sidebar open on a conversation, with the metadata card at the top and the Chat and Observability tabs below it Everything here covers deployed end-user chats as well as your own Agent Studio sessions. There is nothing extra to enable — a conversation from an embedded deployment or an MCP client shows up with its **Platform** set accordingly. ## Next steps Inspect the step-by-step trace behind any answer, and the evaluations that graded it. Add example queries and charts to fix patterns you identify in past conversations. # Data Model & Schema Source: https://docs.upsolve.ai/ai-agent-builder/data-model-schema Choose which tables and columns the agent can see, and encode what they mean. The Data Model is the foundation of your agent's knowledge — every other entry point in this section ([System Prompts](/ai-agent-builder/system-prompts), [Golden Assets](/ai-agent-builder/golden-assets), [Skills](/ai-agent-builder/skills), [Knowledge Base](/ai-agent-builder/knowledge-base), [User Memory](/ai-agent-builder/user-memory), and [MCP](/ai-agent-builder/mcp-context)) builds on top of it. It controls two things: what data the agent can access, and what context it has to interpret that data correctly. A well-configured data model is the difference between an agent that writes plausible SQL and one that writes the *right* SQL for your business. ## Selecting tables and columns Not every table in your database is relevant to every question your agent will answer. Narrowing the schema down to what matters reduces ambiguity, improves query accuracy, and avoids the agent making inferences across data it shouldn't touch. Table selection panel showing checked and unchecked tables From the **Tables** tab in your Data Model, select the tables you want to expose to the agent. By default all tables from your connection are listed — check only the ones that are relevant to the use case you're building for. Within each table, you can further control which columns are included. Expand a table to see its columns, then uncheck any that are unnecessary, sensitive, or likely to confuse the agent. The column list shows the name, data type, and any description you've added. Less is more here. An agent scoped to 5 relevant tables with clear descriptions will significantly outperform one with access to 50 tables it has to guess about. ## Adding table and column descriptions Descriptions are how you encode institutional knowledge directly into the schema. The agent reads these when generating SQL — they're the mechanism for telling it what your data actually means, not just what it's called. Column list showing descriptions and selectable toggles **Table descriptions** provide context for the whole table — what it represents, how it's populated, any important caveats. Click into a table and use the **Table Description** field at the top of the column panel to add this. **Column descriptions** sit inline next to each column. Click the description field for any column and add a plain-English explanation. Useful descriptions answer the question a developer would have when first looking at the column: what does this value represent, what are the units, is there anything non-obvious about how it's calculated? For example: * `contact_name` → *"Name of the customer's point of contact for this order"* * `owner_name` → *"Name of the sales rep (seller) who owns this account"* * `arr` → *"Annual Recurring Revenue in USD, calculated as MRR × 12. Excludes one-time fees."* The more specific your descriptions, the more reliably the agent will apply the right column to the right question. ## Marking columns as selectable The **Selectable** toggle controls whether the agent can scan a column's distinct values at query time to inform how it builds SQL. Column row showing the Selectable toggle in the on and off states When a column is marked selectable, the agent can run `DISTINCT()` over it to extract its unique values, and then use those values in two ways: * **Categorical filtering in SQL writing** — the agent knows the valid filter values *before* it writes a `WHERE` clause. It knows that `region` contains "North America", "EMEA", and "APAC", so it filters on real values rather than guessing at spellings or inventing categories. * **Text grep search** — the agent can search across a selectable column's values to find the exact entry a user referred to loosely (e.g. matching "acme" to the `customer_name` value "Acme Corp (US)"). Think of it as giving the agent the ability to power a select-style filter: it knows the possible values and can use them precisely. Mark a column as selectable when: * It contains categorical values users would naturally filter by (region, product, owner, status) * The distinct values are finite and meaningful to business questions * You want the agent to be able to enumerate or search options rather than guess at valid values Leave it off for high-cardinality columns (IDs, free-text fields, timestamps) where scanning distinct values would be noisy or meaningless. ## Beyond the schema: the Knowledge Base tab The Data Model editor has three tabs — **Tables**, **Global Data Security**, and **Knowledge Base**. Descriptions on tables and columns cover what your *data* means, but some context isn't about any one column: business policies, company background, definitions of terms users use loosely, and known caveats about the data. Those live in the Knowledge Base as searchable markdown documents that ship with each data model version. See [Knowledge Base](/ai-agent-builder/knowledge-base) for how to add and organize them. ## Saving your changes Changes to the data model create a new draft version. Click **Save Version** in the top-right corner when you're ready to checkpoint your work. A data model version must be marked as **Production** before an agent can use it in a live environment. Agents are linked to a specific data model version. Updating the data model does not automatically update agents that reference it — you'll need to update the agent's data model link and set a new production version. ## Next steps Encode business rules, terminology, and behavioral guardrails into the agent. Add example queries and charts the agent can reference when answering similar questions. Store reference documents the agent searches on demand to interpret your terms. Package procedures and conventions the agent loads when it needs them. Let the agent record and apply each end user's preferences automatically. Connect external tools and live systems as additional agent context. # Data Models Source: https://docs.upsolve.ai/ai-agent-builder/data-models Create curated views of your database with descriptions, annotations, and versioning. ## What is a Data Model? A Data Model is a curated view of your database that: * **Selects** which tables and columns are available * **Describes** fields with human-readable annotations * **Secures** data with row-level security rules * **Versions** changes so you can track and rollback Data model showing selected tables and columns ## Why Use Data Models? 1. **Simplify for AI** - Only expose relevant tables so AI agents aren't overwhelmed 2. **Add context** - Descriptions help AI understand what columns mean 3. **Control access** - Data Security rules ensure users see only their data 4. **Safe deployments** - Versioning prevents breaking changes ## Creating a Data Model 1. Navigate to your project's **Data Models** tab 2. Click **Create Data Model** 3. Select the connection to base it on 4. Enter a name for your data model 5. Click **Create** The system will fetch all tables and columns from your connection automatically. Create data model dialog ## Configuring Your Data Model ### Selecting Tables and Columns By default, all tables are selected. To customize: 1. Open your data model 2. Uncheck tables you want to hide 3. Expand tables to uncheck specific columns 4. Click **Save Changes** Table and column selection interface ### Adding Descriptions Help AI and users understand your data: 1. Click the edit icon next to a table or column 2. Enter a description 3. For AI agents, mark columns as "selectable" if they contain categorical values users might filter by Adding description to a column ## Data Security Data Security controls which rows of data each user can access. You can define **Global Security Rules** that apply across all tables, or configure per-table rules for specific needs. ### Understanding Data Security The Data Security system uses SQL-based filtering with dynamic variables: * **`{{user.*}}`** - Access properties of the currently authenticated user * **`{{organization.*}}`** - Access properties of the user's organization For example, to filter an `orders` table so users only see their own orders: ```sql theme={null} SELECT * FROM "orders" WHERE "user_id" = '{{user.id}}' ``` ### Global Security Rules Global Security Rules let you define filtering logic once and apply it to all applicable tables automatically. This is ideal when you have consistent patterns like: * Multi-tenant data separated by `tenant_id` * User-owned data filtered by `user_id` * Schema-based isolation (each tenant has their own database schema) #### Accessing Global Rules 1. Open your data model 2. Click the **Global Data Security** tab, alongside **Tables** and **Knowledge Base** 3. You'll see the Global Rules editor, with **+ Add Rule** above any rules that already exist #### Rule Types **Schema Rules** - Dynamic schema prefixing for multi-tenant databases where each tenant has their own schema: The Schema Rule form with Schema Property set to user.source, and an Example Output line showing every query prefixed with that schema This generates SQL like: ```sql theme={null} SELECT * FROM "{{user.schema}}"."orders" ``` **Column Rules** - Automatic WHERE clause filtering based on column names: The Column Rule form filtering the country column on user.country, with the Optional Rule toggle on, a Tables Affected summary, and the generated WHERE clause This generates SQL like: ```sql theme={null} SELECT * FROM "orders" WHERE "user_id" = '{{user.id}}' ``` #### Creating a Global Rule 1. Click the **Global Data Security** tab 2. Click **Add Rule** 3. Choose **Schema Rule** or **Column Rule** 4. Configure the rule settings 5. The rule is automatically enabled The Add Global Rule dialog offering Schema Rule and Column Rule, each with a one-line description #### How Rules Are Applied * **Schema rules** affect all tables - they change where data is fetched from * **Column rules** only affect tables that have the specified column - tables without the column are unaffected * Multiple rules are combined with AND logic * Rules are applied in priority order (lower priority number = applied first) Applying the rules opens a preview first: the SQL each table will be given, a count of how many will be filtered, and — because a manual override always wins — how many will be skipped. The Apply Global RLS Rules preview, listing the SQL each table will be given, with a count of the tables to be filtered and those skipped for having a manual override ### Per-Table Data Security Selecting a table under **Tables** gives it its own **Data Security** sub-tab — distinct from the model-wide **Global Data Security** tab — showing how the global rules apply to that one table. A table's Data Security sub-tab managed by the global rules, its Applied Rules list empty because this table has no column the rules match, above the read-only generated query #### Manual Overrides (Break Glass) Sometimes you need custom logic for a specific table. You can override global rules: 1. Select the table under **Tables** 2. Go to its **Data Security** sub-tab 3. Click **Edit manually** 4. Confirm the warning dialog 5. Edit the SQL directly The Switch to Manual Security dialog, listing that the generated SQL is copied to the editor, the table stops inheriting global rules, and later global changes will not reach it Once in manual mode, you have full control over the SQL: The same tab in manual override mode: an amber manually-edited banner with Reset to global, an editable query, and the user and organization properties available to it #### Resetting to Global Rules To return a table to global rule management: 1. Click **Reset to global** 2. Confirm the dialog 3. Your manual edits will be replaced with the auto-generated SQL The Reset to Global Security dialog, listing that the manual SQL is deleted, the global rules are re-applied, and later global changes will affect the table again ### Testing Data Security You can test your security rules to see exactly what data a specific user would see: 1. Select a table under **Tables** 2. Go to its **Data Security** sub-tab 3. Select a user from the top-right user dropdown 4. Click **Test Query** 5. View the filtered results Test RLS Policy run as a supplier manager whose user.country resolves to Canada and Mexico, showing the query sent to the database and the 18 rows it returned Testing uses your actual database connection and shows real data filtered by the security rules. This helps verify your configuration before deploying. ## Versioning Every change to a data model creates a new version. This means: * You can see the history of changes * You can compare versions * Agents and applications link to specific versions ### Viewing Versions 1. Open your data model 2. Click the **Versions** tab in the sidebar 3. See all versions with timestamps and change information Data model versions list ## Production Status Before agents and applications can use a data model in production, you must mark a version as "Production." ### Setting Production 1. Open your data model 2. Go to the **Versions** tab 3. Click **Set as Production** on the version you want 4. The system validates the schema against your connection 5. If successful, the version is now production Set version as production button ### Validation When setting production, the system checks: * All tables in your data model exist in the connection * All columns in your data model exist in their tables * If validation fails, you'll see which tables/columns are missing If your database schema changes, you may need to update your data model to match. ## Syncing with Connection Changes If your database schema changes: 1. Open your data model 2. Go to the **Connection** tab 3. Click **Refresh Schema** 4. New tables/columns will appear (you can select them) 5. Missing tables/columns will show warnings Sync with connection showing new and missing items ## Data Model Errors If a production data model has errors (e.g., missing tables), you'll see a warning banner. This typically happens when: * A table was dropped from the database * A column was renamed or removed * The connection credentials changed ## Next Steps * [Create an Agent](/ai-agent-builder/agents) that uses this data model * [Build an Application](/ai-agent-builder/applications) for dashboards * [Manage Project Users](/ai-agent-builder/users) to define user properties for Data Security # Backend Setup Source: https://docs.upsolve.ai/ai-agent-builder/deploy-agents/backend-setup Set up user authorization in the backend for agent deployment ## User Authorization The purpose of user authorization set up in the backend is for Upsolve AI to provide appropriate data access based on your user permissioning set up. In your product's authentication flow, you register users with Upsolve using the [project user registration flow](/embedded-bi/data-permissioning/project-user-registration-flow): register an [organization](/api-reference/endpoint/register-project-organization) once per customer entity, register a [user](/api-reference/endpoint/register-project-user) once per account, then fetch a short-lived [user token](/api-reference/endpoint/get-project-user-token) on every login. The token tells Upsolve AI who the authorized user is and what data they can access, and is used to provide the appropriate access to the agent and underlying data. To generate your API Key please go to the deploy page. ## Project User Token When embedding an Application Space (via the `https://ai-hub.upsolve.ai/share/application/:applicationId` iFrame), you need a **project user token**. This token identifies a specific user within a project organisation and is used by the embed to load their personalised Space. ### Endpoint ``` POST https://api.upsolve.ai/v1/api/projects/user-token ``` ### Request | Field | Type | Required | Description | | ---------------- | ------------- | -------- | --------------------------------------------------------------------- | | `userId` | string (UUID) | Yes | The project user's ID (found in your project's user list) | | `organizationId` | string (UUID) | Yes | The organisation ID the user belongs to | | `apiKey` | string | No | Your Upsolve embed API key (if not passed via `Authorization` header) | | `expiration` | number | No | Token lifetime in seconds (default: 3600) | ### Response ```json theme={null} { "data": { "token": "" } } ``` ### Example ```typescript theme={null} const response = await fetch("https://api.upsolve.ai/v1/api/projects/user-token", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer up_embed_...", // Your embed API key }, body: JSON.stringify({ userId: "user-uuid-here", organizationId: "org-uuid-here", }), }); const { data } = await response.json(); const projectUserToken = data.token; ``` Pass `projectUserToken` as the `jwt` query parameter in the Application iFrame `src`. See [Frontend Setup](/ai-agent-builder/deploy-agents/frontend-setup) for the full embed example. Project user tokens expire after 1 hour by default. Refresh them server-side before they expire and update the iFrame `src` to avoid session interruptions. New integrations should use `POST /v1/api/projects/user-token` directly. Legacy tenant-based integrations remain supported, but the project user flow is the recommended path going forward. ## Setup Inspection You could inspect whether the user authorization is setup successfully. Navigate to the **Deploy** application using the side navigation bar. upsolve-deploy If the endpoint is successfully called, you should see your new tenants in the **Deploy** application: tenants # Canvas Iframe Embed Source: https://docs.upsolve.ai/ai-agent-builder/deploy-agents/canvas-iframe-embed Embed a public canvas into your product with an iframe ## Overview You can embed a public Agent Canvas into your own product using a standard HTML ` ``` * Replace `{accessToken}` with the token from your share modal. * `?hideHeader=1` hides the canvas name and "Read-only" badge for a chrome-free embed (see [URL Parameters](#url-parameters)). * For on-premise deployments, replace `ai-hub.upsolve.ai` with your instance's hostname. That's it. The `/shared/canvas/:token` route sends `Content-Security-Policy: frame-ancestors *`, so the page can be embedded from any origin. Your users see **your** app's page in the address bar; the canvas renders inside the frame. You do **not** need a custom domain or any DNS changes for this. ## (Optional) White-label the share link with your own domain By default the share modal generates links against `ai-hub.upsolve.ai`. If you'd rather the copyable link show your own domain, set a **Custom Share Domain**: 1. Go to **Project Settings → Sharing**. 2. Enter your **Custom Share Domain** — the base that replaces the `ai-hub.upsolve.ai/shared/canvas` portion of the link, e.g. `insights.yourcompany.com/canvas`. A live preview shows the resulting link. 3. Save. From this point on, every canvas's public link resolves to `https:///` — e.g. `https://insights.yourcompany.com/canvas/{accessToken}`. The Custom Share Domain **only rewrites the link string** Upsolve generates — it does not change where the canvas is served. You decide what that URL does: * **Serve it from your own app (no DNS setup).** Route that path in your product (e.g. `/canvas/:token`) to a page you build that iframes the Upsolve canvas — the token is right there in the URL, so your page just renders ` ``` For **Select Multi** filter types, the value **must** be a JSON array, not a plain string. For example `filter_region=%5B%22North%20America%22%5D` (which is `["North America"]` URL-encoded). Plain strings cause chart load errors. ### Multiple Controlled Filters Combine multiple `filter_` parameters in the same URL: ```html theme={null} ``` For the full URL parameter reference — including omnibar (`f_`) filters and combining both types — see [URL Filter Parameters](/embedded-bi/filters/url-filter-parameters). ## Visibility in Embedded Contexts By default, controlled filters are **hidden** when your dashboard is embedded, keeping them invisible to end users. If you need to show a controlled filter in embedded contexts (such as admin panels or internal tools), set **`isVisible: true`** on the filter's control configuration in the Hub. The filter will then render as a visible pill while still being driven by your `filter_` URL parameter. ## Best Practices 1. **Use Descriptive Keys**: Choose filter keys that clearly indicate their purpose (e.g., `userRegion`, `departmentFilter`) 2. **Document Your Filters**: Keep track of which filters are controlled and their expected values 3. **JSON-encode values**: Always JSON-encode (and URL-encode) the value, especially arrays for Select Multi filters ## Troubleshooting ### Controlled Filter Not Working * Verify the filter key matches exactly between the Hub configuration and your `filter_{controlKey}` URL parameter * Check that the filter value type matches the filter configuration (string, number, array, etc.) * For Select Multi filters, ensure the value is a JSON-encoded array (`%5B%22value%22%5D`), not a plain string ### Controlled Filter Hidden When It Should Be Visible * Set `isVisible: true` on the filter's control configuration in the Hub to override the default hiding behavior # Filtering Source: https://docs.upsolve.ai/embedded-bi/filters/filters-in-depth An in depth guide for setting up filters ## Overview Dashboards in Upsolve are **source agnostic**. This means that multiple charts backed by multiple data sources may co-exist on the same dashboard. This provides endless flexibility to dashboard builders, but also requires filtering to be built on a set of rails that is different from traditional BI tools. The basic concept is that there are [chart filters](#chart-filters) and [dashboard filters](#dashboard-filters). A **dashboard** filter can only be configured to point to one or more **chart** filters. This ensures a consistent behavior for the chart, whether it is being viewed/filtered as a standalone experience, or as part of a dashboard. ## Chart filters To create a chart filter, go into the chart editor and select the `Configure filters` tab. Filter Guide 1 There are 3 things you can filter on: **1. Raw backing tables:** filter any column in the raw data that is powering this specific chart **2. Computed table:** filter any column from the data table that was computed by the SQL written for this chart **3. Variables in the chart SQL:** inject variables into the SQL code that is backing this chart Filter Guide 2 There are 3 steps to configuring a filter: **1. Table:** select the table to filter or "Query variables" **2. Column:** select the column or variable to filter on **3. Filter:** configure the filter as needed. Options will be provided based on the type of filter selected Filter Guide 3 Once the filter is added, navigate back to the `Chart Builder` tab to check it out. Click the blue filter icon to see all available filters. Setting one will add the filter to your chart Filter Guide 4 If you are happy with your result, `Save` the chart and let's configure this in the dashboard! ## Dashboard filters To create a dashboard filter, go into the dashboard editor and click the `+ New filter` button. Filter Guide 5 Give your filter a name, and set the type and subtype. In this case, we are going to set a `Select` filter with multiple options allowed. Filter Guide 6 Note that there are only 2 charts on this dashboard that have `Select` filters avaialble for us to map to. Select the ones that this dashboard filter should map to. In this case, it will be `Expense Status 2` in both `Total Spend ($) (Copy)` and `Daily Spend Trend (Copy)`. Click the **green (+) button** next to each filter you want to target and you will see this: Filter Guide 7 Filter Guide 8 Now, there are more expense statuses I want to map to, but the charts that are in this dashboard don't yet have those filters created on them. We can add them from this menu. Click the `# charts with no Select filter shown` button to reveal the other charts. Pick the one you want, such as `Spend by Merchant (Copy)`. Configure it to be a `Select` filter and preview to make sure things worked. Filter Guide 9 Add the filter and hit `Save` to make sure it's added. You will see the filter show up above the panel you are in with a green **(+)** icon. Note that if this filter was not configured as a `Select` filter, it would not appear in this case. This is because chart filters are only shown if they match the dashboard filter type exactly. Filter Guide 10 You may now click the **green (+) icon** to add this newly configured chart filter to the target list of this filter Filter Guide 11 Finally, use the filter preview to confirm that everything is working as expected. Filter Guide 12 Ther filter may now be set on the dashboard level. Note that all charts that the filter is connected to are filtered. Filter Guide 13 # Timezones Source: https://docs.upsolve.ai/embedded-bi/filters/timezone Configure timezones for your dashboard ## Overview Dashboards provide flexible timezone configurations to ensure your data is displayed in a way that best suits your end users' needs. By default, dashboards automatically adjust to the **local timezone of the end user**, contextualizing data for their location. ## Key Requirements * **Data Storage in UTC:** Ensure all timestamps in your data source are stored in **UTC**. Upsolve automatically adjusts these timestamps to match the configured timezone. * **Automatic Adjustment:** Date columns will automatically shift to reflect the end user's local timezone unless otherwise specified. ## Setting a Specific Timezone If you need all end users to see data in the same timezone, regardless of their location, you can configure a specific timezone through the **dashboard builder settings**. ### Steps to Set a Timezone: 1. Open the **Settings** dropdown in the dashboard builder. 2. Select the desired timezone from the available options. 3. To revert to displaying data in the end user's local timezone, select the **Local** option. By setting a specific timezone, your dashboard provides a consistent view of time-sensitive data for all users. Timezone light Timezone dark ## Notes * Dashboards always assume data is stored in **UTC** and adjust based on the selected timezone. * Switching back to the **Local** option ensures data reflects the end user's local timezone automatically. # URL Filter Parameters Source: https://docs.upsolve.ai/embedded-bi/filters/url-filter-parameters Pre-filter shared dashboards via URL query parameters ## Overview When embedding dashboards in iframes or sharing application links, you can pre-set filter values directly in the URL. This lets you: * Show different data views to different users without building separate dashboards * Deep-link to a specific filtered state * Control filters programmatically from your host application ## Omnibar Filters (`f_` prefix) The primary way to pass filter values via URL. These filters show as interactive pills in the filter bar — users can see what's filtered and modify or remove them. ### URL Format ``` f_{sourceType}.{tableName}.{columnName}={typePrefix}:{value} ``` **Source types:** * `Raw` — a column from a raw database table * `Output` — a computed/output column from chart SQL * `Variable` — an RLS variable filter ### Value Types | Prefix | Type | Example | | ------ | --------------------- | ----------------------------------------- | | `s:` | Select (single value) | `s:Athletics` | | `sm:` | Select (multi-select) | `sm:Athletics` or `sm:Athletics,Swimming` | | `n:` | Numeric | `n:2000` | | `b:` | Boolean | `b:true` | | `i:` | Text input | `i:search term` | | `dp:` | Date preset | `dp:last_7_days` | | `ds:` | Date (single) | `ds:2024-01-01` | | `dr:` | Date range | `dr:2024-01-01,2024-12-31` | ### Examples **Single filter:** ```html theme={null}